From 957ac1dc03bf6a250fad08f24e323076049a9994 Mon Sep 17 00:00:00 2001 From: Mazin Date: Tue, 9 May 2023 22:38:11 -0400 Subject: [PATCH] Add translate.nostr.wine to available translation services Closes: https://github.com/damus-io/damus/pull/1113 Signed-off-by: William Casarin --- damus/Models/TranslationService.swift | 3 +++ damus/Models/UserSettingsStore.swift | 14 +++++++++++ damus/Util/Translator.swift | 25 +++++++++++++++++++ .../Settings/TranslationSettingsView.swift | 11 ++++++++ 4 files changed, 53 insertions(+) diff --git a/damus/Models/TranslationService.swift b/damus/Models/TranslationService.swift index 368c06c0..896540d2 100644 --- a/damus/Models/TranslationService.swift +++ b/damus/Models/TranslationService.swift @@ -32,6 +32,7 @@ enum TranslationService: String, CaseIterable, Identifiable, StringCodable { case libretranslate case deepl case nokyctranslate + case winetranslate var model: Model { switch self { @@ -43,6 +44,8 @@ enum TranslationService: String, CaseIterable, Identifiable, StringCodable { return .init(tag: self.rawValue, displayName: NSLocalizedString("DeepL (Proprietary, Higher Accuracy)", comment: "Dropdown option for selecting DeepL as the translation service.")) case .nokyctranslate: return .init(tag: self.rawValue, displayName: NSLocalizedString("NoKYCTranslate.com (Prepay with BTC)", comment: "Dropdown option for selecting NoKYCTranslate.com as the translation service.")) + case .winetranslate: + return .init(tag: self.rawValue, displayName: NSLocalizedString("translate.nostr.wine (DeepL, Pay with BTC)", comment: "Dropdown option for selecting translate.nostr.wine as the translation service.")) } } } diff --git a/damus/Models/UserSettingsStore.swift b/damus/Models/UserSettingsStore.swift index 09473f6b..2bab8c1e 100644 --- a/damus/Models/UserSettingsStore.swift +++ b/damus/Models/UserSettingsStore.swift @@ -244,6 +244,15 @@ class UserSettingsStore: ObservableObject { internal_nokyctranslate_api_key = newValue == "" ? nil : newValue } } + + var winetranslate_api_key: String { + get { + return internal_winetranslate_api_key ?? "" + } + set { + internal_winetranslate_api_key = newValue == "" ? nil : newValue + } + } // These internal keys are necessary because entries in the keychain need to be Optional, // but the translation view needs non-Optional String in order to use them as Bindings. @@ -252,6 +261,9 @@ class UserSettingsStore: ObservableObject { @KeychainStorage(account: "nokyctranslate_apikey") var internal_nokyctranslate_api_key: String? + + @KeychainStorage(account: "winetranslate_apikey") + var internal_winetranslate_api_key: String? @KeychainStorage(account: "libretranslate_apikey") var internal_libretranslate_api_key: String? @@ -269,6 +281,8 @@ class UserSettingsStore: ObservableObject { return internal_deepl_api_key != nil case .nokyctranslate: return internal_nokyctranslate_api_key != nil + case .winetranslate: + return internal_winetranslate_api_key != nil } } } diff --git a/damus/Util/Translator.swift b/damus/Util/Translator.swift index 08f78f3a..3b734f48 100644 --- a/damus/Util/Translator.swift +++ b/damus/Util/Translator.swift @@ -26,6 +26,8 @@ public struct Translator { return try await translateWithLibreTranslate(text, from: sourceLanguage, to: targetLanguage) case .nokyctranslate: return try await translateWithNoKYCTranslate(text, from: sourceLanguage, to: targetLanguage) + case .winetranslate: + return try await translateWithWineTranslate(text, from: sourceLanguage, to: targetLanguage) case .deepl: return try await translateWithDeepL(text, from: sourceLanguage, to: targetLanguage) case .none: @@ -111,6 +113,29 @@ public struct Translator { return response.translatedText } + private func translateWithWineTranslate(_ text: String, from sourceLanguage: String, to targetLanguage: String) async throws -> String? { + let url = try makeURL("https://translate.nostr.wine", path: "/translate") + + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + + struct RequestBody: Encodable { + let q: String + let source: String + let target: String + let api_key: String? + } + let body = RequestBody(q: text, source: sourceLanguage, target: targetLanguage, api_key: userSettingsStore.winetranslate_api_key) + request.httpBody = try encoder.encode(body) + + struct Response: Decodable { + let translatedText: String + } + let response: Response = try await decodedData(for: request) + return response.translatedText + } + private func makeURL(_ baseUrl: String, path: String) throws -> URL { guard var components = URLComponents(string: baseUrl) else { throw URLError(.badURL) diff --git a/damus/Views/Settings/TranslationSettingsView.swift b/damus/Views/Settings/TranslationSettingsView.swift index 14bc125c..be4699f4 100644 --- a/damus/Views/Settings/TranslationSettingsView.swift +++ b/damus/Views/Settings/TranslationSettingsView.swift @@ -73,6 +73,17 @@ struct TranslationSettingsView: View { Link(NSLocalizedString("Get API Key with BTC/Lightning", comment: "Button to navigate to nokyctranslate website to get a translation API key."), destination: URL(string: "https://nokyctranslate.com")!) } } + + if settings.translation_service == .winetranslate { + SecureField(NSLocalizedString("API Key (required)", comment: "Prompt for required entry of API Key to use translation server."), text: $settings.winetranslate_api_key) + .disableAutocorrection(true) + .disabled(settings.translation_service != .winetranslate) + .autocapitalization(UITextAutocapitalizationType.none) + + if settings.winetranslate_api_key == "" { + Link(NSLocalizedString("Get API Key with BTC/Lightning", comment: "Button to navigate to translate.nostr.wine to get a translation API key."), destination: URL(string: "https://translate.nostr.wine")!) + } + } if settings.translation_service != .none { Toggle(NSLocalizedString("Automatically translate notes", comment: "Toggle to automatically translate notes."), isOn: $settings.auto_translate)