mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-06-14 19:29:31 +00:00
Add the ability to translate using deepl even if the instance offers its own service (#1237)
* Allow forced translation with DeepL Translation with DeepL can now be forced either per post or on the system level. Signed-off-by: Paul Schuetz <pa.schuetz@web.de> * Require the use of a private API key A private API key of the user is now required to allow "always translate via DeepL". Signed-off-by: Paul Schuetz <pa.schuetz@web.de> * Persist a stored API key An API key is stored even if useOnlyDeepL is disabled. If the API key is empty, the setting is still disabled. Signed-off-by: Paul Schuetz <pa.schuetz@web.de> * Localize the texts Signed-off-by: Paul Schuetz <pa.schuetz@web.de> * Save API key while writing The API key is now saved, even if the app is closed before leaving the translation settings view. Signed-off-by: Paul Schuetz <pa.schuetz@web.de> * Fix build * Fix theme * Transition to KeychainSwift, clean up KeychainHelper is replaced with the already-used KeychainSwift package, the functions are cleaned up so that the process is easier to understand. The deactivateToggleIfNoKey function doesn't change the behavior of the buttons or context menus in the timeline, only demonstrates the necessity of an API key to the user. Consequently, it's only called when the settings view is shown. Signed-off-by: Paul Schuetz <pa.schuetz@web.de> * Swiftformat + fixes --------- Signed-off-by: Paul Schuetz <pa.schuetz@web.de> Co-authored-by: Thomas Ricouard <ricouard77@gmail.com>
This commit is contained in:
parent
f263c57858
commit
baf853f46e
|
@ -65,6 +65,7 @@
|
|||
9F7D939A29805DBD00EE6B7A /* AccountSettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F7D939929805DBD00EE6B7A /* AccountSettingView.swift */; };
|
||||
9F8CA5972979B61100481E8E /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E9B576C529743F4C00BCE646 /* Localizable.strings */; };
|
||||
9F8CA5982979B63D00481E8E /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E9B576C529743F4C00BCE646 /* Localizable.strings */; };
|
||||
9FA6FD6229C04A8800E2312C /* TranslationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FA6FD6129C04A8800E2312C /* TranslationSettingsView.swift */; };
|
||||
9FAD85832971BF7200496AB1 /* Secret.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9FAD85822971BF7200496AB1 /* Secret.plist */; };
|
||||
9FAD858B29743F7400496AB1 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FAD858A29743F7400496AB1 /* ShareViewController.swift */; };
|
||||
9FAD858E29743F7400496AB1 /* (null) in Resources */ = {isa = PBXBuildFile; };
|
||||
|
@ -225,6 +226,7 @@
|
|||
9F7D939929805DBD00EE6B7A /* AccountSettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountSettingView.swift; sourceTree = "<group>"; };
|
||||
9F7D939B2980F5C100EE6B7A /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = tr; path = tr.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
9F7D939C2980F5C200EE6B7A /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
9FA6FD6129C04A8800E2312C /* TranslationSettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TranslationSettingsView.swift; sourceTree = "<group>"; };
|
||||
9FAD85822971BF7200496AB1 /* Secret.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Secret.plist; sourceTree = "<group>"; };
|
||||
9FAD858829743F7400496AB1 /* IceCubesShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = IceCubesShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
9FAD858A29743F7400496AB1 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
|
||||
|
@ -524,6 +526,7 @@
|
|||
9FAE4ACA293783B000772766 /* SettingsTab.swift */,
|
||||
9F2A540629699698009B2D7C /* SupportAppView.swift */,
|
||||
D08A9C3429956CFA00204A4A /* SwipeActionsSettingsView.swift */,
|
||||
9FA6FD6129C04A8800E2312C /* TranslationSettingsView.swift */,
|
||||
);
|
||||
path = Settings;
|
||||
sourceTree = "<group>";
|
||||
|
@ -819,6 +822,7 @@
|
|||
069709AA298C9AD7006E4CB5 /* AboutView.swift in Sources */,
|
||||
9F2B92FC295DA94500DE16D0 /* InstanceInfoView.swift in Sources */,
|
||||
C9B22677297F6C2E001F9EFE /* ContentSettingsView.swift in Sources */,
|
||||
9FA6FD6229C04A8800E2312C /* TranslationSettingsView.swift in Sources */,
|
||||
9F35DB4C2952005C00B3281A /* MessagesTab.swift in Sources */,
|
||||
9FAD85CF2975B68900496AB1 /* SideBarView.swift in Sources */,
|
||||
9FAE4ACB293783B000772766 /* SettingsTab.swift in Sources */,
|
||||
|
|
|
@ -145,6 +145,9 @@ struct SettingsTabs: View {
|
|||
NavigationLink(destination: SwipeActionsSettingsView()) {
|
||||
Label("settings.general.swipeactions", systemImage: "hand.draw")
|
||||
}
|
||||
NavigationLink(destination: TranslationSettingsView()) {
|
||||
Label("settings.general.translate", systemImage: "captions.bubble")
|
||||
}
|
||||
Link(destination: URL(string: UIApplication.openSettingsURLString)!) {
|
||||
Label("settings.system", systemImage: "gear")
|
||||
}
|
||||
|
|
74
IceCubesApp/App/Tabs/Settings/TranslationSettingsView.swift
Normal file
74
IceCubesApp/App/Tabs/Settings/TranslationSettingsView.swift
Normal file
|
@ -0,0 +1,74 @@
|
|||
import DesignSystem
|
||||
import Env
|
||||
import SwiftUI
|
||||
|
||||
struct TranslationSettingsView: View {
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
||||
@State private var apiKey: String = ""
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Toggle(isOn: preferences.$alwaysUseDeepl) {
|
||||
Label("settings.translation.always-deepl", systemImage: "captions.bubble")
|
||||
}
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
|
||||
if preferences.alwaysUseDeepl {
|
||||
Section("settings.translation.user-api-key") {
|
||||
Picker("settings.translation.api-key-type", selection: $preferences.userDeeplAPIFree) {
|
||||
Text("DeepL API Free").tag(true)
|
||||
Text("DeepL API Pro").tag(false)
|
||||
}
|
||||
|
||||
SecureField("settings.translation.user-api-key", text: $apiKey)
|
||||
.textContentType(.password)
|
||||
}
|
||||
.onAppear(perform: readValue)
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
|
||||
if apiKey.isEmpty {
|
||||
Section {
|
||||
Link(destination: URL(string: "https://www.deepl.com/pro-api")!) {
|
||||
Text("settings.translation.needed-message")
|
||||
.foregroundColor(.red)
|
||||
}
|
||||
}
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(theme.secondaryBackgroundColor)
|
||||
.onChange(of: apiKey, perform: writeNewValue)
|
||||
.onAppear(perform: updatePrefs)
|
||||
}
|
||||
|
||||
private func writeNewValue() {
|
||||
writeNewValue(value: apiKey)
|
||||
}
|
||||
|
||||
private func writeNewValue(value: String) {
|
||||
DeepLUserAPIHandler.write(value: value)
|
||||
}
|
||||
|
||||
private func readValue() {
|
||||
if let apiKey = DeepLUserAPIHandler.readIfAllowed() {
|
||||
self.apiKey = apiKey
|
||||
} else {
|
||||
apiKey = ""
|
||||
}
|
||||
}
|
||||
|
||||
private func updatePrefs() {
|
||||
DeepLUserAPIHandler.deactivateToggleIfNoKey()
|
||||
}
|
||||
}
|
||||
|
||||
struct TranslationSettingsView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
TranslationSettingsView()
|
||||
.environmentObject(UserPreferences.shared)
|
||||
}
|
||||
}
|
|
@ -165,6 +165,11 @@
|
|||
"settings.other.hide-openai" = "Уключыць 🤖 памочніка";
|
||||
"settings.other.social-keyboard" = "Уключыць сацыяльную клавіятуру";
|
||||
"settings.other.sound-effect" = "Enable Sound Effects";
|
||||
"settings.general.translate" = "Translation Settings";
|
||||
"settings.translation.always-deepl" = "Always Translate using DeepL";
|
||||
"settings.translation.user-api-key" = "DeepL API Key";
|
||||
"settings.translation.api-key-type" = "Type of the Key";
|
||||
"settings.translation.needed-message" = "This feature requires a DeepL API key";
|
||||
"settings.push.duplicate.title" = "Выпраўляльнік дублікатаў апавяшчэнняў";
|
||||
"settings.push.duplicate.footer" = "Атрымліваеш падвоеныя апавяшчэнні? Паспрабуй гэтую чароўную кнопку каб выправіць";
|
||||
"settings.push.duplicate.button.fix" = "🪄 Выправіць";
|
||||
|
@ -390,6 +395,7 @@
|
|||
|
||||
// MARK: Package: Status
|
||||
"status.action.translate" = "Перакласці";
|
||||
"status.action.translate-with-deepl" = "Translate with DeepL";
|
||||
"status.action.translated-label-%@" = "Пераклад з дапамогай %@";
|
||||
"status.action.translated-label-from-%@-%@" = "Translated from %@ using %@";
|
||||
"status.action.bookmark" = "Закладка";
|
||||
|
|
|
@ -160,6 +160,11 @@
|
|||
"settings.other.hide-openai" = "Activa l'ajudant 🤖";
|
||||
"settings.other.social-keyboard" = "Activa el teclat social";
|
||||
"settings.other.sound-effect" = "Enable Sound Effects";
|
||||
"settings.general.translate" = "Translation Settings";
|
||||
"settings.translation.always-deepl" = "Always Translate using DeepL";
|
||||
"settings.translation.user-api-key" = "DeepL API Key";
|
||||
"settings.translation.api-key-type" = "Type of the Key";
|
||||
"settings.translation.needed-message" = "This feature requires a DeepL API key";
|
||||
"settings.push.duplicate.title" = "Solucionador de notificacions duplicades";
|
||||
"settings.push.duplicate.footer" = "Rebeu les notificacions duplicades? Proveu aquest botó màgic per a solucionar-ho";
|
||||
"settings.push.duplicate.button.fix" = "🪄 Soluciona-ho";
|
||||
|
@ -384,6 +389,7 @@
|
|||
|
||||
// MARK: Package: Status
|
||||
"status.action.translate" = "Tradueix";
|
||||
"status.action.translate-with-deepl" = "Translate with DeepL";
|
||||
"status.action.translated-label-%@" = "Traduït amb %@";
|
||||
"status.action.translated-label-from-%@-%@" = "Translated from %@ using %@";
|
||||
"status.action.bookmark" = "Afegeix als marcadors";
|
||||
|
|
|
@ -141,6 +141,11 @@
|
|||
"settings.other.hide-openai" = "Aktiviere 🤖-Helfer";
|
||||
"settings.other.social-keyboard" = "Soziale Tastatur aktivieren";
|
||||
"settings.other.sound-effect" = "Klänge aktivieren";
|
||||
"settings.general.translate" = "Übersetzungseinstellungen";
|
||||
"settings.translation.always-deepl" = "Immer mit DeepL übersetzen";
|
||||
"settings.translation.user-api-key" = "DeepL API Schlüssel";
|
||||
"settings.translation.api-key-type" = "Typ des Schlüssels";
|
||||
"settings.translation.needed-message" = "Für diese Funktion ist ein DeepL API-Schlüssel erforderlich.";
|
||||
"settings.general.content" = "Inhaltseinstellungen";
|
||||
"settings.system" = "Systemeinstellungen";
|
||||
"settings.content.navigation-title" = "Inhaltseinstellungen";
|
||||
|
@ -381,6 +386,7 @@
|
|||
|
||||
// MARK: Package: Status
|
||||
"status.action.translate" = "Übersetzen";
|
||||
"status.action.translate-with-deepl" = "Mit DeepL übersetzen";
|
||||
"status.action.translated-label-%@" = "Übersetzt mit %@";
|
||||
"status.action.translated-label-from-%@-%@" = "Aus %@ mit %@ übersetzt";
|
||||
"status.action.bookmark" = "Lesezeichen setzen";
|
||||
|
|
|
@ -166,6 +166,11 @@
|
|||
"settings.other.hide-openai" = "Enable 🤖 Helper";
|
||||
"settings.other.social-keyboard" = "Enable Social Keyboard";
|
||||
"settings.other.sound-effect" = "Enable Sound Effects";
|
||||
"settings.general.translate" = "Translation Settings";
|
||||
"settings.translation.always-deepl" = "Always Translate using DeepL";
|
||||
"settings.translation.user-api-key" = "DeepL API Key";
|
||||
"settings.translation.api-key-type" = "Type of the Key";
|
||||
"settings.translation.needed-message" = "This feature requires a DeepL API key";
|
||||
"settings.push.duplicate.title" = "Duplicate Notifications Fixer";
|
||||
"settings.push.duplicate.footer" = "Receiving duplicate notifications? Try this magic button in order to fix it";
|
||||
"settings.push.duplicate.button.fix" = "🪄 Fix It";
|
||||
|
@ -387,6 +392,7 @@
|
|||
|
||||
// MARK: Package: Status
|
||||
"status.action.translate" = "Translate";
|
||||
"status.action.translate-with-deepl" = "Translate with DeepL";
|
||||
"status.action.translated-label-%@" = "Translated using %@";
|
||||
"status.action.translated-label-from-%@-%@" = "Translated from %@ using %@";
|
||||
"status.action.bookmark" = "Bookmark";
|
||||
|
|
|
@ -165,6 +165,11 @@
|
|||
"settings.other.hide-openai" = "Enable 🤖 Helper";
|
||||
"settings.other.social-keyboard" = "Enable Social Keyboard";
|
||||
"settings.other.sound-effect" = "Enable Sound Effects";
|
||||
"settings.general.translate" = "Translation Settings";
|
||||
"settings.translation.always-deepl" = "Always Translate using DeepL";
|
||||
"settings.translation.user-api-key" = "DeepL API Key";
|
||||
"settings.translation.api-key-type" = "Type of the Key";
|
||||
"settings.translation.needed-message" = "This feature requires a DeepL API key";
|
||||
"settings.push.duplicate.title" = "Duplicate Notifications Fixer";
|
||||
"settings.push.duplicate.footer" = "Receiving duplicate notifications? Try this magic button in order to fix it";
|
||||
"settings.push.duplicate.button.fix" = "🪄 Fix It";
|
||||
|
@ -386,6 +391,7 @@
|
|||
|
||||
// MARK: Package: Status
|
||||
"status.action.translate" = "Translate";
|
||||
"status.action.translate-with-deepl" = "Translate with DeepL";
|
||||
"status.action.translated-label-%@" = "Translated using %@";
|
||||
"status.action.translated-label-from-%@-%@" = "Translated from %@ using %@";
|
||||
"status.action.bookmark" = "Bookmark";
|
||||
|
|
|
@ -141,6 +141,11 @@
|
|||
"settings.other.hide-openai" = "Activar ayudante 🤖";
|
||||
"settings.other.social-keyboard" = "Activar teclado social";
|
||||
"settings.other.sound-effect" = "Enable Sound Effects";
|
||||
"settings.general.translate" = "Translation Settings";
|
||||
"settings.translation.always-deepl" = "Always Translate using DeepL";
|
||||
"settings.translation.user-api-key" = "DeepL API Key";
|
||||
"settings.translation.api-key-type" = "Type of the Key";
|
||||
"settings.translation.needed-message" = "This feature requires a DeepL API key";
|
||||
"settings.general.content" = "Ajustes de contenido";
|
||||
"settings.system" = "Ajustes del sistema";
|
||||
"settings.content.navigation-title" = "Ajustes de contenido";
|
||||
|
@ -386,6 +391,7 @@
|
|||
|
||||
// MARK: Package: Status
|
||||
"status.action.translate" = "Traducir";
|
||||
"status.action.translate-with-deepl" = "Translate with DeepL";
|
||||
"status.action.translated-label-%@" = "Traducido usando %@";
|
||||
"status.action.translated-label-from-%@-%@" = "Traducido desde %@ usando %@";
|
||||
"status.action.bookmark" = "Añadir a marcadores";
|
||||
|
|
|
@ -141,6 +141,11 @@
|
|||
"settings.other.hide-openai" = "Gaitu 🤖 laguntzailea";
|
||||
"settings.other.social-keyboard" = "Gaitu teklatu soziala";
|
||||
"settings.other.sound-effect" = "Gaitu soinu efektuak";
|
||||
"settings.general.translate" = "Translation Settings";
|
||||
"settings.translation.always-deepl" = "Always Translate using DeepL";
|
||||
"settings.translation.user-api-key" = "DeepL API Key";
|
||||
"settings.translation.api-key-type" = "Type of the Key";
|
||||
"settings.translation.needed-message" = "This feature requires a DeepL API key";
|
||||
"settings.general.content" = "Edukiaren ezarpenak";
|
||||
"settings.system" = "Sistemaren ezarpenak";
|
||||
"settings.content.navigation-title" = "Edukiaren ezarpenak";
|
||||
|
@ -379,6 +384,7 @@
|
|||
|
||||
// MARK: Package: Status
|
||||
"status.action.translate" = "Itzuli";
|
||||
"status.action.translate-with-deepl" = "Translate with DeepL";
|
||||
"status.action.translated-label-%@" = "%@ erabiliz itzulia";
|
||||
"status.action.translated-label-from-%@-%@" = "%@(e)tik %@ erabiliz itzulia";
|
||||
"status.action.bookmark" = "Jarri laster-marka";
|
||||
|
|
|
@ -161,6 +161,11 @@
|
|||
"settings.other.hide-openai" = "Activer 🤖 aide";
|
||||
"settings.other.social-keyboard" = "Activer le clavier social";
|
||||
"settings.other.sound-effect" = "Enable Sound Effects";
|
||||
"settings.general.translate" = "Translation Settings";
|
||||
"settings.translation.always-deepl" = "Always Translate using DeepL";
|
||||
"settings.translation.user-api-key" = "DeepL API Key";
|
||||
"settings.translation.api-key-type" = "Type of the Key";
|
||||
"settings.translation.needed-message" = "This feature requires a DeepL API key";
|
||||
"settings.push.duplicate.title" = "Correcteur de notifications en double";
|
||||
"settings.push.duplicate.footer" = "Recevez-vous des notifications en double ? Essayez ce bouton magique pour résoudre le problème";
|
||||
"settings.push.duplicate.button.fix" = "🪄 Résoudre";
|
||||
|
@ -381,6 +386,7 @@
|
|||
|
||||
// MARK: Package: Status
|
||||
"status.action.translate" = "Traduire";
|
||||
"status.action.translate-with-deepl" = "Translate with DeepL";
|
||||
"status.action.translated-label-%@" = "Traduit avec %@";
|
||||
"status.action.translated-label-from-%@-%@" = "Translated from %@ using %@";
|
||||
"status.action.bookmark" = "Marquer";
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
"enum.status-actions-display.only-buttons" = "Solo bottoni";
|
||||
"enum.status-display-style.compact" = "Compatto";
|
||||
"enum.status-display-style.large" = "Completo";
|
||||
"enum.status-display-style.medium" = "Medio";
|
||||
"enum.swipeactions.icon-with-text" = "Icon e testo";
|
||||
"enum.swipeactions.icon-only" = "Solo icone";
|
||||
|
||||
|
@ -141,6 +140,11 @@
|
|||
"settings.other.hide-openai" = "Abilita l'aiuto del 🤖";
|
||||
"settings.other.social-keyboard" = "Abilita social keyboard";
|
||||
"settings.other.sound-effect" = "Attiva gli effetti sonori";
|
||||
"settings.general.translate" = "Translation Settings";
|
||||
"settings.translation.always-deepl" = "Always Translate using DeepL";
|
||||
"settings.translation.user-api-key" = "DeepL API Key";
|
||||
"settings.translation.api-key-type" = "Type of the Key";
|
||||
"settings.translation.needed-message" = "This feature requires a DeepL API key";
|
||||
"settings.general.content" = "Impostazioni dei contenuti";
|
||||
"settings.system" = "Vai alle impostazioni di sistema";
|
||||
"settings.content.navigation-title" = "Impostazioni dei contenuti";
|
||||
|
@ -386,6 +390,7 @@
|
|||
|
||||
// MARK: Package: Status
|
||||
"status.action.translate" = "Traduci";
|
||||
"status.action.translate-with-deepl" = "Translate with DeepL";
|
||||
"status.action.translated-label-%@" = "Tradotto usando %@";
|
||||
"status.action.translated-label-from-%@-%@" = "Tradotto da %@ usando %@";
|
||||
"status.action.bookmark" = "Salva nei segnalibri";
|
||||
|
|
|
@ -165,6 +165,11 @@
|
|||
"settings.other.hide-openai" = "AI支援機能の有効化";
|
||||
"settings.other.social-keyboard" = "ソーシャルメディア向けキーボードの有効化";
|
||||
"settings.other.sound-effect" = "サウンドエフェクトを有効化";
|
||||
"settings.general.translate" = "Translation Settings";
|
||||
"settings.translation.always-deepl" = "Always Translate using DeepL";
|
||||
"settings.translation.user-api-key" = "DeepL API Key";
|
||||
"settings.translation.api-key-type" = "Type of the Key";
|
||||
"settings.translation.needed-message" = "This feature requires a DeepL API key";
|
||||
"settings.push.duplicate.title" = "重複通知修正ツール";
|
||||
"settings.push.duplicate.footer" = "重複して通知を受け取っていませんか?修正するためにこの魔法のボタンを試してみて";
|
||||
"settings.push.duplicate.button.fix" = "🪄 修正する";
|
||||
|
@ -385,6 +390,7 @@
|
|||
|
||||
// MARK: Package: Status
|
||||
"status.action.translate" = "翻訳";
|
||||
"status.action.translate-with-deepl" = "Translate with DeepL";
|
||||
"status.action.translated-label-%@" = "%@ を使用して翻訳";
|
||||
"status.action.translated-label-from-%@-%@" = "%@ から %@ を使用して翻訳";
|
||||
"status.action.bookmark" = "ブックマーク";
|
||||
|
|
|
@ -161,6 +161,11 @@
|
|||
"settings.other.hide-openai" = "글 작성 도우미 🤖";
|
||||
"settings.other.social-keyboard" = "SNS 키보드";
|
||||
"settings.other.sound-effect" = "효과음";
|
||||
"settings.general.translate" = "Translation Settings";
|
||||
"settings.translation.always-deepl" = "Always Translate using DeepL";
|
||||
"settings.translation.user-api-key" = "DeepL API Key";
|
||||
"settings.translation.api-key-type" = "Type of the Key";
|
||||
"settings.translation.needed-message" = "This feature requires a DeepL API key";
|
||||
"settings.push.duplicate.title" = "중복 알림 해결사";
|
||||
"settings.push.duplicate.footer" = "같은 알림이 여러 번 오나요? 위에 있는 버튼을 누르면 마법처럼 해결될 거에요.";
|
||||
"settings.push.duplicate.button.fix" = "🪄 고치기";
|
||||
|
@ -387,6 +392,7 @@
|
|||
|
||||
// MARK: Package: Status
|
||||
"status.action.translate" = "번역";
|
||||
"status.action.translate-with-deepl" = "Translate with DeepL";
|
||||
"status.action.translated-label-%@" = "%@ 서비스를 통해 번역됨";
|
||||
"status.action.translated-label-from-%@-%@" = "%2$@ 서비스를 통해 %1$@에서 번역됨";
|
||||
"status.action.bookmark" = "보관함에 추가";
|
||||
|
|
|
@ -165,6 +165,11 @@
|
|||
"settings.other.hide-openai" = "Aktiver 🤖-hjelper";
|
||||
"settings.other.social-keyboard" = "Aktiver sosialt tastatur";
|
||||
"settings.other.sound-effect" = "Enable Sound Effects";
|
||||
"settings.general.translate" = "Translation Settings";
|
||||
"settings.translation.always-deepl" = "Always Translate using DeepL";
|
||||
"settings.translation.user-api-key" = "DeepL API Key";
|
||||
"settings.translation.api-key-type" = "Type of the Key";
|
||||
"settings.translation.needed-message" = "This feature requires a DeepL API key";
|
||||
"settings.push.duplicate.title" = "Reparasjon av dupliserte varslinger";
|
||||
"settings.push.duplicate.footer" = "Får du dupliserte varsler? Prøv denne magiske knappen for å fikse det.";
|
||||
"settings.push.duplicate.button.fix" = "🪄 Fiks det";
|
||||
|
@ -385,6 +390,7 @@
|
|||
|
||||
// MARK: Package: Status
|
||||
"status.action.translate" = "Oversett";
|
||||
"status.action.translate-with-deepl" = "Translate with DeepL";
|
||||
"status.action.translated-label-%@" = "Oversatt ved hjelp av %@";
|
||||
"status.action.translated-label-from-%@-%@" = "Translated from %@ using %@";
|
||||
"status.action.bookmark" = "Bokmerk";
|
||||
|
|
|
@ -141,6 +141,11 @@
|
|||
"settings.other.hide-openai" = "Gebruik 🤖-hulp";
|
||||
"settings.other.social-keyboard" = "Gebruik socialmedia-toetsenbord";
|
||||
"settings.other.sound-effect" = "Geluidseffecten";
|
||||
"settings.general.translate" = "Translation Settings";
|
||||
"settings.translation.always-deepl" = "Always Translate using DeepL";
|
||||
"settings.translation.user-api-key" = "DeepL API Key";
|
||||
"settings.translation.api-key-type" = "Type of the Key";
|
||||
"settings.translation.needed-message" = "This feature requires a DeepL API key";
|
||||
"settings.general.content" = "Inhoud";
|
||||
"settings.system" = "Systeeminstellingen";
|
||||
"settings.content.navigation-title" = "Inhoud";
|
||||
|
@ -379,6 +384,7 @@
|
|||
|
||||
// MARK: Package: Status
|
||||
"status.action.translate" = "Vertaal";
|
||||
"status.action.translate-with-deepl" = "Translate with DeepL";
|
||||
"status.action.translated-label-%@" = "Vertaald met behulp van %@";
|
||||
"status.action.translated-label-from-%@-%@" = "Vertaald uit het %@ met behulp van %@";
|
||||
"status.action.bookmark" = "Voeg bladwijzer toe";
|
||||
|
|
|
@ -162,6 +162,11 @@
|
|||
"settings.other.social-keyboard" = "Włącz klawiaturę społecznościową";
|
||||
"settings.other.sound-effect" = "Włącz efekty dźwiękowe";
|
||||
"settings.push.duplicate.title" = "Korektor duplikatów powiadomień";
|
||||
"settings.general.translate" = "Translation Settings";
|
||||
"settings.translation.always-deepl" = "Always Translate using DeepL";
|
||||
"settings.translation.user-api-key" = "DeepL API Key";
|
||||
"settings.translation.api-key-type" = "Type of the Key";
|
||||
"settings.translation.needed-message" = "This feature requires a DeepL API key";
|
||||
"settings.push.duplicate.footer" = "Otrzymujesz zduplikowane powiadomienia? Spróbuj tego magicznego przycisku, aby to naprawić";
|
||||
"settings.push.duplicate.button.fix" = "🪄 Napraw to";
|
||||
"settings.other.autoplay-video" = "Odtwarzaj filmy automatycznie";
|
||||
|
@ -381,6 +386,7 @@
|
|||
|
||||
// MARK: Package: Status
|
||||
"status.action.translate" = "Przetłumacz";
|
||||
"status.action.translate-with-deepl" = "Translate with DeepL";
|
||||
"status.action.translated-label-%@" = "Przetłumaczono za pomocą %@";
|
||||
"status.action.translated-label-from-%@-%@" = "Tekst %@ przetłumaczono za pomocą %@";
|
||||
"status.action.bookmark" = "Dodaj zakładkę";
|
||||
|
|
|
@ -161,6 +161,11 @@
|
|||
"settings.other.hide-openai" = "Habilitar 🤖 ajudante";
|
||||
"settings.other.social-keyboard" = "Habilitar Teclado Social";
|
||||
"settings.other.sound-effect" = "Enable Sound Effects";
|
||||
"settings.general.translate" = "Translation Settings";
|
||||
"settings.translation.always-deepl" = "Always Translate using DeepL";
|
||||
"settings.translation.user-api-key" = "DeepL API Key";
|
||||
"settings.translation.api-key-type" = "Type of the Key";
|
||||
"settings.translation.needed-message" = "This feature requires a DeepL API key";
|
||||
"settings.push.duplicate.title" = "Corretor de notificações duplicadas";
|
||||
"settings.push.duplicate.footer" = "Recebendo notificações duplicadas? Tente este botão mágico para tentar corrigir";
|
||||
"settings.push.duplicate.button.fix" = "🪄 Corrigir";
|
||||
|
@ -385,6 +390,7 @@
|
|||
|
||||
// MARK: Package: Status
|
||||
"status.action.translate" = "Traduzir";
|
||||
"status.action.translate-with-deepl" = "Translate with DeepL";
|
||||
"status.action.translated-label-%@" = "Traduzir usando %@";
|
||||
"status.action.translated-label-from-%@-%@" = "Traduzido de %@ usando %@";
|
||||
"status.action.bookmark" = "Salvar";
|
||||
|
|
|
@ -161,6 +161,11 @@
|
|||
"settings.other.hide-openai" = "Yardımcıyı 🤖 Aktive Et";
|
||||
"settings.other.social-keyboard" = "Sosyal Klavyeyi Aktive Et";
|
||||
"settings.other.sound-effect" = "Enable Sound Effects";
|
||||
"settings.general.translate" = "Translation Settings";
|
||||
"settings.translation.always-deepl" = "Always Translate using DeepL";
|
||||
"settings.translation.user-api-key" = "DeepL API Key";
|
||||
"settings.translation.api-key-type" = "Type of the Key";
|
||||
"settings.translation.needed-message" = "This feature requires a DeepL API key";
|
||||
"settings.push.duplicate.title" = "Duplicate notifications fixer";
|
||||
"settings.push.duplicate.footer" = "Receiving duplicate notifications? Try this magic button in order to fix it";
|
||||
"settings.push.duplicate.button.fix" = "🪄 Fix it";
|
||||
|
@ -381,6 +386,7 @@
|
|||
|
||||
// MARK: Package: Status
|
||||
"status.action.translate" = "Tercüme et";
|
||||
"status.action.translate-with-deepl" = "Translate with DeepL";
|
||||
"status.action.translated-label-%@" = "%@ tarafından tercüme edildi";
|
||||
"status.action.translated-label-from-%@-%@" = "Translated from %@ using %@";
|
||||
"status.action.bookmark" = "Yer İmi Ekle";
|
||||
|
|
|
@ -165,6 +165,11 @@
|
|||
"settings.other.hide-openai" = "Увімкнути 🤖 помічника";
|
||||
"settings.other.social-keyboard" = "Увімкнути Social Keyboard";
|
||||
"settings.other.sound-effect" = "Увімкнути звукові ефекти";
|
||||
"settings.general.translate" = "Translation Settings";
|
||||
"settings.translation.always-deepl" = "Always Translate using DeepL";
|
||||
"settings.translation.user-api-key" = "DeepL API Key";
|
||||
"settings.translation.api-key-type" = "Type of the Key";
|
||||
"settings.translation.needed-message" = "This feature requires a DeepL API key";
|
||||
"settings.push.duplicate.title" = "Виправити задвоєння сповіщень";
|
||||
"settings.push.duplicate.footer" = "Отримуєте сповіщення двічі? Спробуйте цю чарівну кнопку, щоб виправити це!";
|
||||
"settings.push.duplicate.button.fix" = "🪄 Виправити!";
|
||||
|
@ -386,6 +391,7 @@
|
|||
|
||||
// MARK: Package: Status
|
||||
"status.action.translate" = "Перекласти";
|
||||
"status.action.translate-with-deepl" = "Translate with DeepL";
|
||||
"status.action.translated-label-%@" = "Переклад з допомогою %@";
|
||||
"status.action.translated-label-from-%@-%@" = "Переклад з %@ з допомогою %@";
|
||||
"status.action.bookmark" = "У закладки";
|
||||
|
|
|
@ -141,6 +141,11 @@
|
|||
"settings.other.hide-openai" = "启用写作助手 🤖";
|
||||
"settings.other.social-keyboard" = "启用社交键盘";
|
||||
"settings.other.sound-effect" = "启用声音效果";
|
||||
"settings.general.translate" = "Translation Settings";
|
||||
"settings.translation.always-deepl" = "Always Translate using DeepL";
|
||||
"settings.translation.user-api-key" = "DeepL API Key";
|
||||
"settings.translation.api-key-type" = "Type of the Key";
|
||||
"settings.translation.needed-message" = "This feature requires a DeepL API key";
|
||||
|
||||
"settings.general.content" = "内容设置";
|
||||
"settings.system" = "系统设置";
|
||||
|
@ -384,6 +389,7 @@
|
|||
|
||||
// MARK: Package: Status
|
||||
"status.action.translate" = "翻译";
|
||||
"status.action.translate-with-deepl" = "Translate with DeepL";
|
||||
"status.action.translated-label-%@" = "由 %@ 翻译";
|
||||
"status.action.translated-label-from-%@-%@" = "翻译自 %@,使用 %@";
|
||||
"status.action.bookmark" = "书签";
|
||||
|
|
|
@ -209,6 +209,12 @@
|
|||
"settings.section.cache" = "快取";
|
||||
"settings.cache-media.clear" = "清除媒體快取";
|
||||
|
||||
"settings.general.translate" = "Translation Settings";
|
||||
"settings.translation.always-deepl" = "Always Translate using DeepL";
|
||||
"settings.translation.user-api-key" = "DeepL API Key";
|
||||
"settings.translation.api-key-type" = "Type of the Key";
|
||||
"settings.translation.needed-message" = "This feature requires a DeepL API key";
|
||||
|
||||
// MARK: Tabs
|
||||
"tab.explore" = "探索";
|
||||
"tab.federated" = "聯邦";
|
||||
|
|
43
Packages/Env/Sources/Env/DeepLUserAPIHandler.swift
Normal file
43
Packages/Env/Sources/Env/DeepLUserAPIHandler.swift
Normal file
|
@ -0,0 +1,43 @@
|
|||
import Foundation
|
||||
import KeychainSwift
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
public enum DeepLUserAPIHandler {
|
||||
private static let key = "DeepL"
|
||||
private static var keychain: KeychainSwift {
|
||||
let keychain = KeychainSwift()
|
||||
#if !DEBUG && !targetEnvironment(simulator)
|
||||
keychain.accessGroup = AppInfo.keychainGroup
|
||||
#endif
|
||||
return keychain
|
||||
}
|
||||
|
||||
public static func write(value: String) {
|
||||
keychain.synchronizable = true
|
||||
if !value.isEmpty {
|
||||
keychain.set(value, forKey: key)
|
||||
} else {
|
||||
keychain.delete(key)
|
||||
}
|
||||
}
|
||||
|
||||
public static func readIfAllowed() -> String? {
|
||||
guard UserPreferences.shared.alwaysUseDeepl else { return nil }
|
||||
|
||||
return readValue()
|
||||
}
|
||||
|
||||
private static func readValue() -> String? {
|
||||
keychain.synchronizable = true
|
||||
return keychain.get(key)
|
||||
}
|
||||
|
||||
public static func deactivateToggleIfNoKey() {
|
||||
UserPreferences.shared.alwaysUseDeepl = shouldAlwaysUseDeepl
|
||||
}
|
||||
|
||||
public static var shouldAlwaysUseDeepl: Bool {
|
||||
readIfAllowed() != nil
|
||||
}
|
||||
}
|
|
@ -26,6 +26,8 @@ public class UserPreferences: ObservableObject {
|
|||
@AppStorage("app_default_post_visibility") public var appDefaultPostVisibility: Models.Visibility = .pub
|
||||
@AppStorage("app_default_posts_sensitive") public var appDefaultPostsSensitive = false
|
||||
@AppStorage("autoplay_video") public var autoPlayVideo = true
|
||||
@AppStorage("always_use_deepl") public var alwaysUseDeepl = false
|
||||
@AppStorage("user_deepl_api_free") public var userDeeplAPIFree = true
|
||||
|
||||
@AppStorage("suppress_dupe_reblogs") public var suppressDupeReblogs: Bool = false
|
||||
|
||||
|
|
|
@ -6,9 +6,17 @@ public struct DeepLClient {
|
|||
case notFound
|
||||
}
|
||||
|
||||
private let endpoint = "https://api.deepl.com/v2/translate"
|
||||
private var deeplUserAPIKey: String?
|
||||
private var deeplUserAPIFree: Bool
|
||||
private var endpoint: String {
|
||||
"https://api\(deeplUserAPIFree && (deeplUserAPIKey != nil) ? "-free" : "").deepl.com/v2/translate"
|
||||
}
|
||||
|
||||
private var APIKey: String {
|
||||
if let deeplUserAPIKey {
|
||||
return deeplUserAPIKey
|
||||
}
|
||||
|
||||
if let path = Bundle.main.path(forResource: "Secret", ofType: "plist") {
|
||||
let secret = NSDictionary(contentsOfFile: path)
|
||||
return secret?["DEEPL_SECRET"] as? String ?? ""
|
||||
|
@ -35,9 +43,12 @@ public struct DeepLClient {
|
|||
return decoder
|
||||
}
|
||||
|
||||
public init() {}
|
||||
public init(userAPIKey: String?, userAPIFree: Bool) {
|
||||
deeplUserAPIKey = userAPIKey
|
||||
deeplUserAPIFree = userAPIFree
|
||||
}
|
||||
|
||||
public func request(target: String, source _: String?, text: String) async throws -> StatusTranslation {
|
||||
public func request(target: String, text: String) async throws -> StatusTranslation {
|
||||
do {
|
||||
var components = URLComponents(string: endpoint)!
|
||||
var queryItems: [URLQueryItem] = []
|
||||
|
|
|
@ -288,28 +288,49 @@ public class StatusRowViewModel: ObservableObject {
|
|||
}
|
||||
|
||||
func translate(userLang: String) async {
|
||||
do {
|
||||
withAnimation {
|
||||
isLoadingTranslation = true
|
||||
}
|
||||
// We first use instance translation API if available.
|
||||
let translation: StatusTranslation = try await client.post(endpoint: Statuses.translate(id: finalStatus.id,
|
||||
lang: userLang))
|
||||
withAnimation {
|
||||
self.translation = translation
|
||||
isLoadingTranslation = false
|
||||
}
|
||||
} catch {
|
||||
// If not or fail we use Ice Cubes own DeepL client.
|
||||
let deepLClient = DeepLClient()
|
||||
let translation = try? await deepLClient.request(target: userLang,
|
||||
source: finalStatus.language,
|
||||
text: finalStatus.content.asRawText)
|
||||
withAnimation {
|
||||
self.translation = translation
|
||||
isLoadingTranslation = false
|
||||
}
|
||||
if !alwaysTranslateWithDeepl {
|
||||
do {
|
||||
withAnimation {
|
||||
isLoadingTranslation = true
|
||||
}
|
||||
// We first use instance translation API if available.
|
||||
let translation: StatusTranslation = try await client.post(endpoint: Statuses.translate(id: finalStatus.id,
|
||||
lang: userLang))
|
||||
withAnimation {
|
||||
self.translation = translation
|
||||
isLoadingTranslation = false
|
||||
}
|
||||
|
||||
return
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// If not or fail we use Ice Cubes own DeepL client.
|
||||
await translateWithDeepL(userLang: userLang)
|
||||
}
|
||||
|
||||
func translateWithDeepL(userLang: String) async {
|
||||
let deepLClient = getDeepLClient()
|
||||
let translation = try? await deepLClient.request(target: userLang,
|
||||
text: finalStatus.content.asRawText)
|
||||
withAnimation {
|
||||
self.translation = translation
|
||||
isLoadingTranslation = false
|
||||
}
|
||||
}
|
||||
|
||||
private func getDeepLClient() -> DeepLClient {
|
||||
let userAPIfree = UserPreferences.shared.userDeeplAPIFree
|
||||
|
||||
return DeepLClient(userAPIKey: userAPIKey, userAPIFree: userAPIfree)
|
||||
}
|
||||
|
||||
private var userAPIKey: String? {
|
||||
DeepLUserAPIHandler.readIfAllowed()
|
||||
}
|
||||
|
||||
var alwaysTranslateWithDeepl: Bool {
|
||||
DeepLUserAPIHandler.shouldAlwaysUseDeepl
|
||||
}
|
||||
|
||||
func fetchRemoteStatus() async -> Bool {
|
||||
|
|
|
@ -137,6 +137,16 @@ struct StatusRowContextMenu: View {
|
|||
} label: {
|
||||
Label("status.action.translate", systemImage: "captions.bubble")
|
||||
}
|
||||
|
||||
if viewModel.alwaysTranslateWithDeepl {
|
||||
Button {
|
||||
Task {
|
||||
await viewModel.translateWithDeepL(userLang: lang)
|
||||
}
|
||||
} label: {
|
||||
Label("status.action.translate-with-deepl", systemImage: "captions.bubble")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if account.account?.id == viewModel.status.reblog?.account.id ?? viewModel.status.account.id {
|
||||
|
|
Loading…
Reference in a new issue