diff --git a/Easydict.xcodeproj/project.pbxproj b/Easydict.xcodeproj/project.pbxproj index db53e1bc8..8c59bd983 100644 --- a/Easydict.xcodeproj/project.pbxproj +++ b/Easydict.xcodeproj/project.pbxproj @@ -57,6 +57,7 @@ 03542A5E2938F05B00C34C33 /* EZLanguageModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 03542A5D2938F05B00C34C33 /* EZLanguageModel.m */; }; 0357B95A2C04387D00A48CB0 /* TextEditorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0357B9592C04387D00A48CB0 /* TextEditorCell.swift */; }; 035E37E72A0953120061DFAF /* EZToast.m in Sources */ = {isa = PBXBuildFile; fileRef = 035E37E62A0953120061DFAF /* EZToast.m */; }; + 035F9CCF2C529A04005D1C9A /* ServiceAPIType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035F9CCE2C529A04005D1C9A /* ServiceAPIType.swift */; }; 0361965529FFECFC00806370 /* youdao-sign.js in Resources */ = {isa = PBXBuildFile; fileRef = 0361965429FFECFC00806370 /* youdao-sign.js */; }; 036196752A000F5900806370 /* FWEncryptorAES.m in Sources */ = {isa = PBXBuildFile; fileRef = 036196702A000F5800806370 /* FWEncryptorAES.m */; }; 036196762A000F5900806370 /* NSData+Base64.m in Sources */ = {isa = PBXBuildFile; fileRef = 036196722A000F5900806370 /* NSData+Base64.m */; }; @@ -301,6 +302,9 @@ C4DD01EB2B12BA250025EE8E /* TencentResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DD01EA2B12BA250025EE8E /* TencentResponse.swift */; }; C4DD01ED2B12BE9B0025EE8E /* TencentTranslateType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DD01EC2B12BE9B0025EE8E /* TencentTranslateType.swift */; }; C4DE3D6D2AC00EB500C2B85D /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = C4DE3D6C2AC00EB500C2B85D /* Localizable.xcstrings */; }; + CB8C42FC2C441B5B004EC86F /* BaiduTranslate+ConfigurableService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB8C42FB2C441B5A004EC86F /* BaiduTranslate+ConfigurableService.swift */; }; + CB8C42FF2C441B74004EC86F /* BaiduApiTranslate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB8C42FD2C441B74004EC86F /* BaiduApiTranslate.swift */; }; + CB8C43002C441B74004EC86F /* BaiduApiResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB8C42FE2C441B74004EC86F /* BaiduApiResponse.swift */; }; DC46DF802B4417B900DEAE3E /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC46DF7F2B4417B900DEAE3E /* Configuration.swift */; }; DC6D9C892B3969510055EFFC /* Appearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC6D9C882B3969510055EFFC /* Appearance.swift */; }; DCF176F22B57CED700CA6026 /* Configuration+UserData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF176F12B57CED700CA6026 /* Configuration+UserData.swift */; }; @@ -432,6 +436,7 @@ 0357B9592C04387D00A48CB0 /* TextEditorCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEditorCell.swift; sourceTree = ""; }; 035E37E52A0953120061DFAF /* EZToast.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EZToast.h; sourceTree = ""; }; 035E37E62A0953120061DFAF /* EZToast.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EZToast.m; sourceTree = ""; }; + 035F9CCE2C529A04005D1C9A /* ServiceAPIType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ServiceAPIType.swift; path = Easydict/Swift/View/SettingView/Tabs/ServiceConfigurationView/ServiceAPIType.swift; sourceTree = SOURCE_ROOT; }; 0361965429FFECFC00806370 /* youdao-sign.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "youdao-sign.js"; sourceTree = ""; }; 0361966F2A000F5800806370 /* NSData+Base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+Base64.h"; sourceTree = ""; }; 036196702A000F5800806370 /* FWEncryptorAES.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FWEncryptorAES.m; sourceTree = ""; }; @@ -803,6 +808,9 @@ C4DD01EC2B12BE9B0025EE8E /* TencentTranslateType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TencentTranslateType.swift; sourceTree = ""; }; C4DE3D6C2AC00EB500C2B85D /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = Localizable.xcstrings; path = Easydict/App/Localizable.xcstrings; sourceTree = SOURCE_ROOT; }; C99EEB182385796700FEE666 /* Easydict-debug.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Easydict-debug.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + CB8C42FB2C441B5A004EC86F /* BaiduTranslate+ConfigurableService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BaiduTranslate+ConfigurableService.swift"; sourceTree = ""; }; + CB8C42FD2C441B74004EC86F /* BaiduApiTranslate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaiduApiTranslate.swift; sourceTree = ""; }; + CB8C42FE2C441B74004EC86F /* BaiduApiResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaiduApiResponse.swift; sourceTree = ""; }; DC46DF7F2B4417B900DEAE3E /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = ""; }; DC6D9C882B3969510055EFFC /* Appearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Appearance.swift; sourceTree = ""; }; DCF176F12B57CED700CA6026 /* Configuration+UserData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Configuration+UserData.swift"; sourceTree = ""; }; @@ -1432,6 +1440,8 @@ 03B0222C29231FA6001C7E63 /* Baidu */ = { isa = PBXGroup; children = ( + CB8C42FE2C441B74004EC86F /* BaiduApiResponse.swift */, + CB8C42FD2C441B74004EC86F /* BaiduApiTranslate.swift */, 03542A412937B45E00C34C33 /* EZBaiduTranslate.h */, 03542A422937B45E00C34C33 /* EZBaiduTranslate.m */, 03542A442937B4C300C34C33 /* EZBaiduTranslateResponse.h */, @@ -2414,6 +2424,7 @@ 0AC8A8462B6A4E3F006DA5CC /* ServiceConfigurationSecretSectionView.swift */, 0AC8A8442B6A4D97006DA5CC /* ServiceConfigurationCells.swift */, 0357B9592C04387D00A48CB0 /* TextEditorCell.swift */, + 035F9CCE2C529A04005D1C9A /* ServiceAPIType.swift */, ); path = ServiceConfigurationView; sourceTree = ""; @@ -2428,6 +2439,7 @@ 0AC8A8382B666F07006DA5CC /* CaiyunService+ConfigurableService.swift */, 0AC8A83A2B6682D4006DA5CC /* AliService+ConfigurableService.swift */, 0AC8A8422B6957B0006DA5CC /* BingService+ConfigurableService.swift */, + CB8C42FB2C441B5A004EC86F /* BaiduTranslate+ConfigurableService.swift */, ); path = ConfigurationView; sourceTree = ""; @@ -2889,6 +2901,7 @@ 03B3B8B52925DD3D00168E8D /* EZPopButtonViewController.m in Sources */, 03542A5B2938DA2B00C34C33 /* EZDetectLanguageButton.m in Sources */, 03B0232929231FA6001C7E63 /* NSDictionary+MM.m in Sources */, + 035F9CCF2C529A04005D1C9A /* ServiceAPIType.swift in Sources */, 03280B812C23FE4A00E75A24 /* StreamConfigurationView.swift in Sources */, 0333FDA32A035BEC00891515 /* NSArray+EZChineseText.m in Sources */, 03B0233229231FA6001C7E63 /* MMLog.swift in Sources */, @@ -2903,6 +2916,7 @@ 0361967B2A0037F700806370 /* NSData+EZMD5.m in Sources */, 967712EE2B5B943400105E0F /* Shortcut.swift in Sources */, 03BFFC68295F4B87004E033E /* EZYoudaoDictModel.m in Sources */, + CB8C42FF2C441B74004EC86F /* BaiduApiTranslate.swift in Sources */, 03247E3A296AE8EC00AFCD67 /* EZLoadingAnimationView.m in Sources */, 0396D615292CC4C3006A11D9 /* EZLocalStorage.m in Sources */, 0329CD6F29EE924500963F78 /* EZRightClickDetector.m in Sources */, @@ -2931,6 +2945,7 @@ 9643D9462B71D103000FBEA6 /* KeyHolderRowView.swift in Sources */, 0399C6A829A74E0F00B4AFCC /* EZQueryResult+EZDeepLTranslateResponse.m in Sources */, 039B694F2A9D9F370063709D /* EZWebViewManager.m in Sources */, + CB8C42FC2C441B5B004EC86F /* BaiduTranslate+ConfigurableService.swift in Sources */, 03B0231629231FA6001C7E63 /* SnipFocusView.m in Sources */, 03FB3EDD2B1B405B004C3238 /* TencentSigning.swift in Sources */, 03B0230329231FA6001C7E63 /* EZResultView.m in Sources */, @@ -2969,6 +2984,7 @@ EAE3D3502B62E9DE001EE3E3 /* GlobalContext.swift in Sources */, EA9943F02B5354C400EE7B97 /* ShowWindowPositionExtensions.swift in Sources */, 96B2F1C52C07782400AD3126 /* RepoInfoHelper.swift in Sources */, + CB8C43002C441B74004EC86F /* BaiduApiResponse.swift in Sources */, 03B0233129231FA6001C7E63 /* MMCrash.m in Sources */, 03B0232629231FA6001C7E63 /* NSAttributedString+MM.m in Sources */, 03542A402937B3C900C34C33 /* EZOCRResult.m in Sources */, diff --git a/Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved index 386bd0dd7..fa9f74761 100644 --- a/Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "7ec61cc8c0ffc99d533d209ed8dd5c63dc918d25ea4172d03761bf84e053e81b", + "originHash" : "c08faef207d4abe4942f46cbbff9587355018cfffbc88badfc29bcb2100a7649", "pins" : [ { "identity" : "abseil-cpp-binary", @@ -87,8 +87,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/google/generative-ai-swift", "state" : { - "revision" : "5d750b80651da9721c37c5eb1fc0b6750d1884d3", - "version" : "0.5.3" + "revision" : "54784005b57c2235a7669e0c12e8dafa68f0ca5f", + "version" : "0.5.4" } }, { diff --git a/Easydict/App/Easydict-Bridging-Header.h b/Easydict/App/Easydict-Bridging-Header.h index 637111de3..7f363cf52 100644 --- a/Easydict/App/Easydict-Bridging-Header.h +++ b/Easydict/App/Easydict-Bridging-Header.h @@ -24,4 +24,5 @@ #import "EZDeepLTranslate.h" #import "EZBingService.h" #import "NSString+EZUtils.h" +#import "EZBaiduTranslate.h" #import "MMCrash.h" diff --git a/Easydict/App/Localizable.xcstrings b/Easydict/App/Localizable.xcstrings index c183145a4..108aca71f 100644 --- a/Easydict/App/Localizable.xcstrings +++ b/Easydict/App/Localizable.xcstrings @@ -427,6 +427,7 @@ } }, "baidu_translate" : { + "comment" : "Baidu Translate", "localizations" : { "en" : { "stringUnit" : { @@ -908,6 +909,23 @@ } } }, + "error_missing_api_key" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Missing API Key" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "缺少 API Key" + } + } + } + }, "error_network" : { "localizations" : { "en" : { @@ -2326,6 +2344,74 @@ } } }, + "service.configuration.api_missing.tips %@" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Currently using the Secret Key API type, but a certain API Key is empty. Please go to Settings > Service > %@, fill in the required configuration information, or switch API types." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "当前正使用 Secret Key API 类型,但是某个 API Key 为空,请前往设置-服务-%@,填写相关配置信息,或切换 API 类型" + } + } + } + }, + "service.configuration.api_picker.title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "API Type" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "API 类型" + } + } + } + }, + "service.configuration.baidu.app_id.title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "App ID" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "App ID" + } + } + } + }, + "service.configuration.baidu.secret_key.title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Secret Key" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Secret Key" + } + } + } + }, "service.configuration.bing.cookie.title" : { "localizations" : { "en" : { @@ -4225,4 +4311,4 @@ } }, "version" : "1.0" -} \ No newline at end of file +} diff --git a/Easydict/Swift/Feature/Configuration/Configuration+Defaults.swift b/Easydict/Swift/Feature/Configuration/Configuration+Defaults.swift index e8bc9bf4f..5ba90ba7e 100644 --- a/Easydict/Swift/Feature/Configuration/Configuration+Defaults.swift +++ b/Easydict/Swift/Feature/Configuration/Configuration+Defaults.swift @@ -246,6 +246,12 @@ extension Defaults.Keys { // Ali static let aliAccessKeyId = Key(EZAliAccessKeyId, default: "") static let aliAccessKeySecret = Key(EZAliAccessKeySecret, default: "") + static let aliServiceApiTypeKey = Key(EZAliServiceApiTypeKey, default: .secretKey) + + // baidu + static let baiduAppId = Key(EZBaiduAppId, default: "") + static let baiduSecretKey = Key(EZBaiduSecretKey, default: "") + static let baiduServiceApiTypeKey = Key(EZBaiduServiceApiTypeKey, default: .secretKey) } /// shortcut diff --git a/Easydict/Swift/Service/Ali/AliService.swift b/Easydict/Swift/Service/Ali/AliService.swift index 63e214742..3e25f40a3 100644 --- a/Easydict/Swift/Service/Ali/AliService.swift +++ b/Easydict/Swift/Service/Ali/AliService.swift @@ -56,10 +56,6 @@ class AliService: QueryService { .ali } - override func hasPrivateAPIKey() -> Bool { - !aliAccessKeyId.isEmpty && !aliAccessKeySecret.isEmpty - } - override func translate( _ text: String, from: Language, @@ -78,13 +74,7 @@ class AliService: QueryService { return } - /** - use user's access key id and secret - easydict://writeKeyValue?EZAliAccessKeyId= - easydict://writeKeyValue?EZAliAccessKeySecret= - */ - if !aliAccessKeyId.isEmpty, - !aliAccessKeySecret.isEmpty { + if Defaults[.aliServiceApiTypeKey] == .secretKey { requestByAPI( id: aliAccessKeyId, secret: aliAccessKeySecret, @@ -152,6 +142,20 @@ class AliService: QueryService { to: Language, completion: @escaping (EZQueryResult, Error?) -> () ) { + if id.isEmpty || secret.isEmpty { + completion( + result, + EZError( + type: EZErrorType.missingAPIKey, + description: String.localizedStringWithFormat( + NSLocalizedString("service.configuration.api_missing.tips %@", comment: "API key missing"), + name() + ) + ) + ) + return + } + func hmacSha1(key: String, params: String) -> String? { guard let secret = key.data(using: .utf8), let what = params.data(using: .utf8) diff --git a/Easydict/Swift/View/SettingView/Tabs/ServiceConfigurationView/ConfigurationView/AliService+ConfigurableService.swift b/Easydict/Swift/View/SettingView/Tabs/ServiceConfigurationView/ConfigurationView/AliService+ConfigurableService.swift index ee711645b..c9be8593e 100644 --- a/Easydict/Swift/View/SettingView/Tabs/ServiceConfigurationView/ConfigurationView/AliService+ConfigurableService.swift +++ b/Easydict/Swift/View/SettingView/Tabs/ServiceConfigurationView/ConfigurationView/AliService+ConfigurableService.swift @@ -11,6 +11,11 @@ import SwiftUI extension AliService { override func configurationListItems() -> Any? { ServiceConfigurationSecretSectionView(service: self, observeKeys: [.aliAccessKeyId, .aliAccessKeySecret]) { + ServiceConfigurationPickerCell( + titleKey: "service.configuration.api_picker.title", + key: .aliServiceApiTypeKey, + values: ServiceAPIType.allCases + ) ServiceConfigurationSecureInputCell( textFieldTitleKey: "service.configuration.ali.access_key_id.title", key: .aliAccessKeyId diff --git a/Easydict/Swift/View/SettingView/Tabs/ServiceConfigurationView/ConfigurationView/BaiduTranslate+ConfigurableService.swift b/Easydict/Swift/View/SettingView/Tabs/ServiceConfigurationView/ConfigurationView/BaiduTranslate+ConfigurableService.swift new file mode 100644 index 000000000..4c2f4cdab --- /dev/null +++ b/Easydict/Swift/View/SettingView/Tabs/ServiceConfigurationView/ConfigurationView/BaiduTranslate+ConfigurableService.swift @@ -0,0 +1,36 @@ +// +// BaiduService+ConfigurableService.swift +// Easydict +// +// Created by karl on 2024/7/13. +// Copyright © 2024 izual. All rights reserved. +// + +import Combine +import Foundation +import SwiftUI + +extension EZBaiduTranslate { + open override func configurationListItems() -> Any { + ServiceConfigurationSecretSectionView( + service: self, + observeKeys: [.baiduAppId, .baiduSecretKey] + ) { + ServiceConfigurationPickerCell( + titleKey: "service.configuration.api_picker.title", + key: .baiduServiceApiTypeKey, + values: ServiceAPIType.allCases + ) + + ServiceConfigurationSecureInputCell( + textFieldTitleKey: "service.configuration.baidu.app_id.title", + key: .baiduAppId + ) + + ServiceConfigurationSecureInputCell( + textFieldTitleKey: "service.configuration.baidu.secret_key.title", + key: .baiduSecretKey + ) + } + } +} diff --git a/Easydict/Swift/View/SettingView/Tabs/ServiceConfigurationView/ServiceAPIType.swift b/Easydict/Swift/View/SettingView/Tabs/ServiceConfigurationView/ServiceAPIType.swift new file mode 100644 index 000000000..b77141268 --- /dev/null +++ b/Easydict/Swift/View/SettingView/Tabs/ServiceConfigurationView/ServiceAPIType.swift @@ -0,0 +1,22 @@ +// +// ServiceAPIType.swift +// Easydict +// +// Created by karl on 2024/7/25. +// Copyright © 2024 izual. All rights reserved. +// + +import Defaults +import Foundation +import SwiftUI + +enum ServiceAPIType: String, CaseIterable, Defaults.Serializable, EnumLocalizedStringConvertible { + case web = "Web API" + case secretKey = "Secret Key API" + + // MARK: Internal + + var title: LocalizedStringKey { + LocalizedStringKey(rawValue) + } +} diff --git a/Easydict/objc/Service/Baidu/BaiduApiResponse.swift b/Easydict/objc/Service/Baidu/BaiduApiResponse.swift new file mode 100644 index 000000000..ba0d3a23b --- /dev/null +++ b/Easydict/objc/Service/Baidu/BaiduApiResponse.swift @@ -0,0 +1,68 @@ +// +// BaiduApiResponse.swift +// Easydict +// +// Created by karl on 2024/7/13. +// Copyright © 2024 izual. All rights reserved. +// + +import Foundation + +// MARK: - BaiduApiResponse + +/* + { + "from": "en", + "to": "zh", + "trans_result": [ + { + "src": "apple", + "dst": "苹果" + } + ] + } + + */ + +struct BaiduApiResponse: Codable { + // MARK: Internal + + struct TransResult: Codable { + var src: String + var dst: String + } + + var from: String + var to: String + var transResult: [TransResult] + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case transResult = "trans_result" + case from + case to + } +} + +// MARK: - BaiduApiErrorResponse + +/* + { + "error_code": "54001", + "error_msg": "Invalid Sign" + } + */ +struct BaiduApiErrorResponse: Codable { + // MARK: Internal + + var errorCode: String + var errorMsg: String + + // MARK: Private + + private enum CodingKeys: String, CodingKey { + case errorCode = "error_code" + case errorMsg = "error_msg" + } +} diff --git a/Easydict/objc/Service/Baidu/BaiduApiTranslate.swift b/Easydict/objc/Service/Baidu/BaiduApiTranslate.swift new file mode 100644 index 000000000..dfcd28375 --- /dev/null +++ b/Easydict/objc/Service/Baidu/BaiduApiTranslate.swift @@ -0,0 +1,115 @@ +// +// EZBaiduApiTranslate.swift +// Easydict +// +// Created by karl on 2024/7/13. +// Copyright © 2024 izual. All rights reserved. +// + +import Alamofire +import Defaults +import Foundation + +@objc(EZBaiduApiTranslate) +@objcMembers +class BaiduApiTranslate: NSObject { + // MARK: Lifecycle + + required public init(queryModel: EZQueryModel) { + self.queryModel = queryModel + super.init() + } + + // MARK: Internal + + var result: EZQueryResult? + + var isEnable: Bool { + Defaults[.baiduServiceApiTypeKey] == ServiceAPIType.secretKey + } + + func translate(_ text: String, from: Language, to: Language, completion: @escaping (EZQueryResult?, Error?) -> ()) { + if appId.isEmpty || secretKey.isEmpty { + completion( + result, + EZError( + type: EZErrorType.missingAPIKey, + description: String.localizedStringWithFormat( + NSLocalizedString("service.configuration.api_missing.tips %@", comment: "API key missing"), + NSLocalizedString("baidu_translate", comment: "Baidu Translate") + ) + ) + ) + return + } + + guard let utf8Data = text.data(using: .utf8), + let utf8String = String(data: utf8Data, encoding: .utf8) else { + logError("Failed to convert text to UTF8") + completion(result, EZError(type: EZErrorType.API, description: "Failed to convert text to UTF8")) + return + } + let salt = UUID().uuidString + let sign = appId + utf8String + salt + secretKey + let signMd5 = sign.md5() + + let param: [String: Any] = [ + "q": utf8String, + "from": from.rawValue, + "to": to.rawValue, + "appid": appId, + "salt": salt, + "sign": signMd5, + ] + + let request = AF.request( + "https://fanyi-api.baidu.com/api/trans/vip/translate", + method: .post, + parameters: param, + headers: [ + "Content-Type": "application/x-www-form-urlencoded", + ] + ) + .validate() + .responseDecodable(of: BaiduApiResponse.self) { [weak self] response in + guard let self else { return } + let result = result ?? EZQueryResult() + result.from = from + result.to = to + result.queryText = text + + switch response.result { + case let .success(value): + result.translatedResults = value.transResult.map { $0.dst } + completion(result, nil) + case let .failure(error): + logError("Baidu official API error \(error)") + let ezError = EZError(nsError: error) + if let data = response.data { + do { + let errorResponse = try JSONDecoder().decode(BaiduApiErrorResponse.self, from: data) + ezError?.errorDataMessage = "code:\(errorResponse.errorCode), msg:\(errorResponse.errorMsg)" + } catch { + logError("Failed to decode error response: \(error)") + } + } + completion(result, ezError) + } + } + queryModel.setStop({ + request.cancel() + }, serviceType: ServiceType.baidu.rawValue) + } + + // MARK: Private + + private let queryModel: EZQueryModel + + private var appId: String { + Defaults[.baiduAppId] + } + + private var secretKey: String { + Defaults[.baiduSecretKey] + } +} diff --git a/Easydict/objc/Service/Baidu/EZBaiduTranslate.m b/Easydict/objc/Service/Baidu/EZBaiduTranslate.m index 027a3f68f..0b7d8cf28 100644 --- a/Easydict/objc/Service/Baidu/EZBaiduTranslate.m +++ b/Easydict/objc/Service/Baidu/EZBaiduTranslate.m @@ -21,6 +21,7 @@ @interface EZBaiduTranslate () @property (nonatomic, strong) JSValue *jsFunction; @property (nonatomic, strong) AFHTTPSessionManager *htmlSession; @property (nonatomic, strong) AFHTTPSessionManager *jsonSession; +@property (nonatomic, strong) EZBaiduApiTranslate *apiTranslate; @property (nonatomic, copy) NSString *token; @property (nonatomic, copy) NSString *gtk; @@ -44,6 +45,8 @@ - (instancetype)init { FIX https://github.com/tisfeng/Easydict/issues/466 */ + + self.apiTranslate = [[EZBaiduApiTranslate alloc] initWithQueryModel:self.queryModel]; } return self; } @@ -121,6 +124,11 @@ - (NSString *)cookie { #pragma mark - 重写父类方法 +- (void)setResult:(EZQueryResult *)result { + [super setResult:result]; + self.apiTranslate.result = result; +} + - (EZServiceType)serviceType { return EZServiceTypeBaidu; } @@ -223,6 +231,11 @@ - (void)translate:(NSString *)text from:(EZLanguage)from to:(EZLanguage)to compl text = [text trimToMaxLength:5000]; + if (self.apiTranslate.isEnable) { + [self.apiTranslate translate:text from:[self languageCodeForLanguage:from] to:[self languageCodeForLanguage:to] completion:completion]; + return; + } + void (^request)(void) = ^(void) { void (^translateBlock)(EZLanguage) = ^(EZLanguage from) { [self sendTranslateRequest:text from:from to:to completion:completion]; diff --git a/Easydict/objc/Service/Model/EZConstKey.h b/Easydict/objc/Service/Model/EZConstKey.h index 60f716567..4884933cc 100644 --- a/Easydict/objc/Service/Model/EZConstKey.h +++ b/Easydict/objc/Service/Model/EZConstKey.h @@ -25,6 +25,11 @@ static NSString *const EZGeminiAPIKey = @"EZGeminiAPIKey"; static NSString *const EZIntelligentQueryModeKey = @"IntelligentQueryMode"; static NSString *const EZAliAccessKeyId = @"EZAliAccessKeyId"; static NSString *const EZAliAccessKeySecret = @"EZAliAccessKeySecret"; +static NSString *const EZAliServiceApiTypeKey = @"EZAliServiceApiTypeKey"; + +static NSString *const EZBaiduAppId = @"EZBaiduAppId"; +static NSString *const EZBaiduSecretKey = @"EZBaiduSecretKey"; +static NSString *const EZBaiduServiceApiTypeKey = @"EZBaiduServiceApiTypeKey"; @interface EZConstKey : NSObject diff --git a/Easydict/objc/Service/Model/EZError.h b/Easydict/objc/Service/Model/EZError.h index 2ca7238f2..153242710 100644 --- a/Easydict/objc/Service/Model/EZError.h +++ b/Easydict/objc/Service/Model/EZError.h @@ -30,6 +30,7 @@ typedef NS_ENUM(NSUInteger, EZErrorType) { EZErrorTypeNoResultsFound, // 未查询到结果 EZErrorTypeInsufficientQuota, // 内置 API key 额度不足 EZErrorTypeWarppedNSError, // Warp NSError + EZErrorTypeMissingAPIKey, // 没有设置API Key }; /// 错误,不支持的语言 diff --git a/Easydict/objc/Service/Model/EZError.m b/Easydict/objc/Service/Model/EZError.m index da7318ad8..6678d439b 100644 --- a/Easydict/objc/Service/Model/EZError.m +++ b/Easydict/objc/Service/Model/EZError.m @@ -78,7 +78,9 @@ + (instancetype)errorWithType:(EZErrorType)type case EZErrorTypeInsufficientQuota: errorString = NSLocalizedString(@"error_insufficient_quota", nil); break; - + case EZErrorTypeMissingAPIKey: + errorString = NSLocalizedString(@"error_missing_api_key", nil); + break; default: errorString = NSLocalizedString(@"error_unknown", nil); break;