Skip to content

Commit

Permalink
Add TencentService Support (#240)
Browse files Browse the repository at this point in the history
* Add basic support for CaiyunService

Add basic implementation for Caiyun Service

Fix CR suggestion

Add caiyun service transtype support

perf: split caiyun test token to more parts

perf: improve caiyun supported langauges dict

Update Easydict/Feature/Service/Caiyun/CaiyunService.swift

Co-authored-by: Kyle <[email protected]>

* Add basic support for CaiyunService

Add basic implementation for Caiyun Service

Fix CR suggestion

Add caiyun service transtype support

perf: split caiyun test token to more parts

perf: improve caiyun supported langauges dict

Update Easydict/Feature/Service/Caiyun/CaiyunService.swift

Co-authored-by: Kyle <[email protected]>

* feat: initiate Tencent Translate support

* feat: continue building Tencent Translate

* fix: error when detecting unsupported languages in transType

* style: code formatting

* feat: proceed to Authentication header

* fix: re-add TencentAPIKek

* Fix header issue

* fix: remove duplicated EZTencentAPIKey

* perf: optimize logic in TencentTranslateType.swift

* perf: optimize and localize error handling in unsupported languages

* perf(UI): add English localization for Tencent Translate

* perf: optimize MMOrderedDictionary logic

* perf(UI): revert TMT icon

* perf: initialize support for secrets customization

* perf: implement demo for authentication

* fix: proper json decoding

* feat: implement secrets customization

* style: format code

Format with SwiftLint

* perf: add self support language

* fix: build error

* perf(UI): adopt official name of Tencent Translate

Co-Authored-By: Tisfeng <[email protected]>

* perf: create headerAuth func for authorization

* perf(UI): delete Tencent TTS in dropdown

* refactor: improve tencent sign

* refractor: improve tencent sign

Co-Authored-By: Tisfeng <[email protected]>

* fix: tencent api endpoint

* Revert "perf: add self support language"

This reverts commit 0bc2ab1.

* perf: update handling of language

* fix: correct supportedTypes

* style: format code

fixed extraneous white spaces with SwiftLint

* perf: prepares TencentSigning for other services

* perf: enable tencent from == to

* perf: add missing traditional Chinese target language

* perf: improve tencent error message

* perf: map error response --> Response

---------

Co-authored-by: Kyle <[email protected]>
Co-authored-by: tisfeng <[email protected]>
  • Loading branch information
3 people committed Dec 16, 2023
1 parent 215fb29 commit 9fc8d0b
Show file tree
Hide file tree
Showing 11 changed files with 428 additions and 3 deletions.
26 changes: 26 additions & 0 deletions Easydict.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@
03F639952AA6CFBB009B9914 /* EZBingConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 03F639942AA6CFBB009B9914 /* EZBingConfig.m */; };
03FD68BB2B1DC59600FD388E /* CryptoSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 03FD68BA2B1DC59600FD388E /* CryptoSwift */; };
03FD68BE2B1E151A00FD388E /* String+EncryptAES.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03FD68BD2B1E151A00FD388E /* String+EncryptAES.swift */; };
03FB3EDD2B1B405B004C3238 /* sign.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03FB3EDC2B1B405B004C3238 /* sign.swift */; };
03FB3EDD2B1B405B004C3238 /* TencentSigning.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03FB3EDC2B1B405B004C3238 /* TencentSigning.swift */; };
17BCAEF72B0DFF9000A7D372 /* EZNiuTransTranslateResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 17BCAEF52B0DFF9000A7D372 /* EZNiuTransTranslateResponse.m */; };
17BCAEF82B0DFF9000A7D372 /* EZNiuTransTranslate.m in Sources */ = {isa = PBXBuildFile; fileRef = 17BCAEF62B0DFF9000A7D372 /* EZNiuTransTranslate.m */; };
2721E4D02AFE920700A059AC /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 2721E4CF2AFE920700A059AC /* Alamofire */; };
Expand All @@ -225,6 +227,9 @@
62ED29A22B15F1F500901F51 /* EZWrapView.m in Sources */ = {isa = PBXBuildFile; fileRef = 62ED29A12B15F1F500901F51 /* EZWrapView.m */; };
A0B65CA0F31AC8ECFB8347CC /* Pods_EasydictTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 378E73A7EA8FC8FB9C975A63 /* Pods_EasydictTests.framework */; };
B87AC7E36367075BA5D13234 /* Pods_Easydict.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6372B33DFF803C7096A82250 /* Pods_Easydict.framework */; };
C4DD01E92B12B3C80025EE8E /* TencentService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DD01E82B12B3C80025EE8E /* TencentService.swift */; };
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 */; };
C98CAE75239F4619005F7DCA /* EasydictHelper.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = C90BE309239F38EB00ADE88B /* EasydictHelper.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
/* End PBXBuildFile section */
Expand Down Expand Up @@ -641,6 +646,8 @@
03F639932AA6CFBB009B9914 /* EZBingConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EZBingConfig.h; sourceTree = "<group>"; };
03F639942AA6CFBB009B9914 /* EZBingConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EZBingConfig.m; sourceTree = "<group>"; };
03FD68BD2B1E151A00FD388E /* String+EncryptAES.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+EncryptAES.swift"; sourceTree = "<group>"; };
03FB3EDC2B1B405B004C3238 /* sign.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = sign.swift; sourceTree = "<group>"; };
03FB3EDC2B1B405B004C3238 /* TencentSigning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TencentSigning.swift; sourceTree = "<group>"; };
06E15747A7BD34D510ADC6A8 /* Pods-Easydict.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Easydict.debug.xcconfig"; path = "Target Support Files/Pods-Easydict/Pods-Easydict.debug.xcconfig"; sourceTree = "<group>"; };
17BCAEF32B0DFF9000A7D372 /* EZNiuTransTranslateResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZNiuTransTranslateResponse.h; sourceTree = "<group>"; };
17BCAEF42B0DFF9000A7D372 /* EZNiuTransTranslate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZNiuTransTranslate.h; sourceTree = "<group>"; };
Expand All @@ -667,6 +674,9 @@
6372B33DFF803C7096A82250 /* Pods_Easydict.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Easydict.framework; sourceTree = BUILT_PRODUCTS_DIR; };
91E3E579C6DB88658B4BB102 /* Pods-Easydict.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Easydict.release.xcconfig"; path = "Target Support Files/Pods-Easydict/Pods-Easydict.release.xcconfig"; sourceTree = "<group>"; };
A230E9A2358C7FBC7FB26189 /* Pods-EasydictTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-EasydictTests.debug.xcconfig"; path = "Target Support Files/Pods-EasydictTests/Pods-EasydictTests.debug.xcconfig"; sourceTree = "<group>"; };
C4DD01E82B12B3C80025EE8E /* TencentService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TencentService.swift; sourceTree = "<group>"; };
C4DD01EA2B12BA250025EE8E /* TencentResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TencentResponse.swift; sourceTree = "<group>"; };
C4DD01EC2B12BE9B0025EE8E /* TencentTranslateType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TencentTranslateType.swift; sourceTree = "<group>"; };
C4DE3D6C2AC00EB500C2B85D /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = Localizable.xcstrings; path = Easydict/App/Localizable.xcstrings; sourceTree = SOURCE_ROOT; };
C4DE3D6E2AC00EB500C2B85D /* mul */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = mul; path = mul.lproj/Main.xcstrings; sourceTree = "<group>"; };
C90BE309239F38EB00ADE88B /* EasydictHelper.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = EasydictHelper.app; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -1172,6 +1182,7 @@
children = (
17BCAEF22B0DFF9000A7D372 /* Niutrans */,
2746AEBF2AF95040005FE0A1 /* Caiyun */,
C4DD01E72B12B3B00025EE8E /* Tencent */,
6220AD582A8280E800BBFB52 /* Bing */,
0399C6A929A8608000B4AFCC /* OpenAI */,
03F14A382956011400CB7379 /* Volcano */,
Expand Down Expand Up @@ -1941,6 +1952,17 @@
name = "Recovered References";
sourceTree = "<group>";
};
C4DD01E72B12B3B00025EE8E /* Tencent */ = {
isa = PBXGroup;
children = (
C4DD01E82B12B3C80025EE8E /* TencentService.swift */,
C4DD01EA2B12BA250025EE8E /* TencentResponse.swift */,
C4DD01EC2B12BE9B0025EE8E /* TencentTranslateType.swift */,
03FB3EDC2B1B405B004C3238 /* TencentSigning.swift */,
);
path = Tencent;
sourceTree = "<group>";
};
C99EEB0F2385796700FEE666 = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -2250,6 +2272,7 @@
03B0232E29231FA6001C7E63 /* MMCrashSignalExceptionHandler.m in Sources */,
03BDA7C42A26DA280079D04F /* NSDictionary+RubyDescription.m in Sources */,
62ED29A22B15F1F500901F51 /* EZWrapView.m in Sources */,
C4DD01EB2B12BA250025EE8E /* TencentResponse.swift in Sources */,
036A0DB82AD8403A006E6D4F /* NSString+EZHandleInputText.m in Sources */,
03BDA7C12A26DA280079D04F /* XPMArgumentParser.m in Sources */,
03B0231329231FA6001C7E63 /* NSView+HiddenDebug.m in Sources */,
Expand Down Expand Up @@ -2350,6 +2373,7 @@
039B694F2A9D9F370063709D /* EZWebViewManager.m in Sources */,
03D747432A07FB150006CD77 /* EZError.m in Sources */,
03B0231629231FA6001C7E63 /* SnipFocusView.m in Sources */,
03FB3EDD2B1B405B004C3238 /* TencentSigning.swift in Sources */,
03B0230329231FA6001C7E63 /* EZResultView.m in Sources */,
03CAB9552ADBF0FF00DA94A3 /* EZSystemUtility.m in Sources */,
03BDA7C32A26DA280079D04F /* NSArray+XPMArgumentsNormalizer.m in Sources */,
Expand All @@ -2376,13 +2400,15 @@
03B0233129231FA6001C7E63 /* MMCrash.m in Sources */,
03B0232629231FA6001C7E63 /* NSAttributedString+MM.m in Sources */,
03542A402937B3C900C34C33 /* EZOCRResult.m in Sources */,
C4DD01E92B12B3C80025EE8E /* TencentService.swift in Sources */,
036A0DBB2AD941F9006E6D4F /* EZReplaceTextButton.m in Sources */,
03DC7C662A3CA465000BF7C9 /* HWSegmentedControl.m in Sources */,
03B022E929231FA6001C7E63 /* AppDelegate.m in Sources */,
03B0232729231FA6001C7E63 /* NSColor+MM.m in Sources */,
03B0233529231FA6001C7E63 /* MMFileLogFormatter.m in Sources */,
03DC38C1292CC97900922CB2 /* EZServiceInfo.m in Sources */,
03B0232A29231FA6001C7E63 /* NSColor+MyColors.m in Sources */,
C4DD01ED2B12BE9B0025EE8E /* TencentTranslateType.swift in Sources */,
03D043562928940500E7559E /* EZBaseQueryWindow.m in Sources */,
03BDA7B92A26DA280079D04F /* NSProcessInfo+XPMArgumentParser.m in Sources */,
03542A4F2937B64B00C34C33 /* EZYoudaoOCRResponse.m in Sources */,
Expand Down
17 changes: 17 additions & 0 deletions Easydict/App/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -2175,6 +2175,23 @@
}
}
},
"tencent_translate" : {
"comment" : "The name of Tencent Translate",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Tencent Translate"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "腾讯翻译君"
}
}
}
},
"toggle_languages" : {
"localizations" : {
"en" : {
Expand Down
3 changes: 2 additions & 1 deletion Easydict/Feature/Service/Model/EZConstKey.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ static NSString *const EZDeepLAuthKey = @"EZDeepLAuthKey";
static NSString *const EZBingCookieKey = @"EZBingCookieKey";
static NSString *const EZNiuTransAPIKey = @"EZNiuTransAPIKey";
static NSString *const EZCaiyunToken = @"EZCaiyunToken";

static NSString *const EZTencentSecretId = @"EZTencentSecretId";
static NSString *const EZTencentSecretKey = @"EZTencentSecretKey";

@interface EZConstKey : NSObject

Expand Down
1 change: 1 addition & 0 deletions Easydict/Feature/Service/Model/EZEnumTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ FOUNDATION_EXPORT EZServiceType const EZServiceTypeAppleDictionary;
FOUNDATION_EXPORT EZServiceType const EZServiceTypeBing;
FOUNDATION_EXPORT EZServiceType const EZServiceTypeNiuTrans;
FOUNDATION_EXPORT EZServiceType const EZServiceTypeCaiyun;
FOUNDATION_EXPORT EZServiceType const EZServiceTypeTencent;

FOUNDATION_EXPORT NSString *const EZQueryTextTypeKey;
FOUNDATION_EXPORT NSString *const EZIntelligentQueryTextTypeKey;
Expand Down
1 change: 1 addition & 0 deletions Easydict/Feature/Service/Model/EZEnumTypes.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
NSString *const EZServiceTypeBing = @"Bing";
NSString *const EZServiceTypeNiuTrans = @"NiuTrans";
NSString *const EZServiceTypeCaiyun = @"Caiyun";
NSString *const EZServiceTypeTencent = @"Tencent";

NSString *const EZServiceTypeAppleDictionary = @"AppleDictionary";

Expand Down
1 change: 1 addition & 0 deletions Easydict/Feature/Service/Model/EZServiceTypes.m
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ + (instancetype)allocWithZone:(struct _NSZone *)zone {
EZServiceTypeVolcano, [EZVolcanoTranslate class],
EZServiceTypeNiuTrans, [EZNiuTransTranslate class],
EZServiceTypeCaiyun, [EZCaiyunService class],
EZServiceTypeTencent, [EZTencentService class],
nil];
return allServiceDict;
}
Expand Down
60 changes: 60 additions & 0 deletions Easydict/Feature/Service/Tencent/TencentResponse.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// TencentResponse.swift
// Easydict
//
// Created by Jerry on 2023-11-25.
// Copyright © 2023 izual. All rights reserved.
//

import Foundation

struct TencentResponse: Codable {
struct Response: Codable {
var RequestId: String
var Source: String
var Target: String
var TargetText: String
}

var Response: Response
}

/**
{
"Response": {
"Error": {
"Code": "InvalidParameterValue",
"Message": "不支持的语种:hi_to_zh"
},
"RequestId": "eb6d17f2-6771-4653-af6f-6b2edbf07294"
}
}
*/
struct TencentErrorResponse: Codable {
struct Response: Codable {
var error: Error
var requestId: String

// CodingKeys 枚举用于映射字段名
private enum CodingKeys: String, CodingKey {
case error = "Error" // error --> Error
case requestId = "RequestId" // requestId --> RequestId
}
}

struct Error: Codable {
var code: String
var message: String

private enum CodingKeys: String, CodingKey {
case code = "Code" // code --> Code
case message = "Message" // message --> Message
}
}

var response: Response

private enum CodingKeys: String, CodingKey {
case response = "Response" // response --> Response
}
}
131 changes: 131 additions & 0 deletions Easydict/Feature/Service/Tencent/TencentService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
//
// TencentService.swift
// Easydict
//
// Created by Jerry on 2023-11-25.
// Copyright © 2023 izual. All rights reserved.
//

import Alamofire
import Foundation

@objc(EZTencentService)
public final class TencentService: QueryService {
override public func serviceType() -> ServiceType {
.tencent
}

override public func link() -> String? {
"https://fanyi.qq.com"
}

override public func name() -> String {
NSLocalizedString("tencent_translate", comment: "The name of Tencent Translate")
}

override public func supportLanguagesDictionary() -> MMOrderedDictionary<AnyObject, AnyObject> {
// TODO: Replace MMOrderedDictionary in the API
let orderedDict = MMOrderedDictionary<AnyObject, AnyObject>()
TencentTranslateType.supportLanguagesDictionary.forEach { key, value in
orderedDict.setObject(value as NSString, forKey: key.rawValue as NSString)
}
return orderedDict
}

override public func ocr(_: EZQueryModel) async throws -> EZOCRResult {
NSLog("Tencent Translate currently does not support OCR")
throw QueryServiceError.notSupported
}

// MARK: API Request
private static let defaultSecretId = ""
private static let defaultSecretKey = ""


// easydict://writeKeyValue?EZTencentSecretId=xxx
private var secretId: String {
let secretId = UserDefaults.standard.string(forKey: EZTencentSecretId)
if let secretId, !secretId.isEmpty {
return secretId
} else {
return TencentService.defaultSecretId
}
}

// easydict://writeKeyValue?EZTencentSecretKey=xxx
private var secretKey: String {
let secretKey = UserDefaults.standard.string(forKey: EZTencentSecretKey)
if let secretKey, !secretKey.isEmpty {
return secretKey
} else {
return TencentService.defaultSecretKey
}
}

public override func translate(_ text: String, from: Language, to: Language, completion: @escaping (EZQueryResult, Error?) -> Void) {
if prehandleQueryTextLanguage(text, from: from, to: to, completion: completion) {
return
}

translateText(text, from: from, to: to, completion: completion)
}

func translateText(_ text: String, from: Language, to: Language, completion: @escaping (EZQueryResult, Error?) -> Void) {
let transType = TencentTranslateType.transType(from: from, to: to)
guard transType != .unsupported else {
result.errorType = .unsupportedLanguage
let unsupportedType = NSLocalizedString("unsupported_translation_type", comment: "")
result.errorMessage = "\(unsupportedType): \(from.rawValue) --> \(to.rawValue)"
completion(result, nil)
return
}

let parameters: [String: Any] = [
"SourceText": text,
"Source": transType.sourceLanguage,
"Target": transType.targetLanguage,
"ProjectId": 0
]

let endpoint = "https://tmt.tencentcloudapi.com"

let service = "tmt"
let action = "TextTranslate"
let version = "2018-03-21"

let headers = tencentSignHeader(service: service, action: action, version: version, parameters: parameters, secretId: secretId, secretKey: secretKey)

let request = AF.request(endpoint,
method: .post,
parameters: parameters,
encoding: JSONEncoding.default,
headers: headers)
.validate()
.responseDecodable(of: TencentResponse.self) { [weak self] response in
guard let self else { return }
let result = self.result
switch response.result {
case let .success(value):
result.from = from
result.to = to
result.queryText = text
result.translatedResults = value.Response.TargetText.components(separatedBy: "\n")
completion(result, nil)
case let .failure(error):
NSLog("Tencent lookup error \(error)")
if let data = response.data {
do {
let errorResponse = try JSONDecoder().decode(TencentErrorResponse.self, from: data)
result.errorMessage = errorResponse.response.error.message
} catch {
NSLog("Failed to decode error response: \(error)")
}
}
completion(result, error)
}
}
queryModel.setStop({
request.cancel()
}, serviceType: serviceType().rawValue)
}
}
Loading

0 comments on commit 9fc8d0b

Please sign in to comment.