From e0c41b5af988b083917cf5de34c634771a9b34ff Mon Sep 17 00:00:00 2001 From: Tomeu Cabot Date: Tue, 17 Dec 2024 18:32:25 +0100 Subject: [PATCH] feat: add cohere as new translate engine --- locales/de.json | 3 ++ locales/en.json | 3 ++ locales/es.json | 3 ++ locales/fr.json | 3 ++ locales/hu.json | 3 ++ locales/ja.json | 3 ++ locales/ko.json | 3 ++ locales/nb-NO.json | 3 ++ locales/nl-NL.json | 3 ++ locales/pt-BR.json | 3 ++ locales/ru.json | 3 ++ locales/sv-SE.json | 3 ++ locales/th.json | 3 ++ locales/tr.json | 3 ++ locales/uk-UA.json | 3 ++ locales/zh-CN.json | 3 ++ locales/zh-TW.json | 3 ++ package.json | 23 +++++++++- src/core/Config.ts | 14 +++++- src/translators/engines/cohere.ts | 73 +++++++++++++++++++++++++++++++ src/translators/index.ts | 3 ++ 21 files changed, 162 insertions(+), 2 deletions(-) create mode 100644 src/translators/engines/cohere.ts diff --git a/locales/de.json b/locales/de.json index fbba4850..3bbff17a 100644 --- a/locales/de.json +++ b/locales/de.json @@ -74,6 +74,9 @@ "config.openai_api_key": "", "config.openai_api_model": "", "config.openai_api_root": "", + "config.cohere_api_key": "", + "config.cohere_api_model": "", + "config.cohere_api_root": "", "config.path_matcher": "Pfad für benutzerdefinierten Namespace. Dokumentation für weitere details prüfen.", "config.preferred_delimiter": "Bevorzugtes Trennzeichen für zusammengesetzte Schlüsse. Standardwert ist \"-\"", "config.prompt_translating_source": "Aufforderung zur Auswahl der Quellsprache bei jeder Verwendung. Wenn false, die Quellsprache wird aus der Konfiguration gelesen.", diff --git a/locales/en.json b/locales/en.json index 6f9c8b38..ba609bd7 100644 --- a/locales/en.json +++ b/locales/en.json @@ -74,6 +74,9 @@ "config.openai_api_key": "OpenAI API key", "config.openai_api_model": "OpenAI chatgpt model", "config.openai_api_root": "OpenAI API root URL", + "config.cohere_api_key": "Cohere API key", + "config.cohere_api_model": "Cohere Chat model", + "config.cohere_api_root": "Cohere API root URL", "config.path_matcher": "Match path for custom locale/namespace. Check out the docs for more details.", "config.preferred_delimiter": "Preferred delimiter of composed keypath, default to \"-\"(dash)", "config.prompt_translating_source": "Prompt to select source locale on translating every time. If set false, the source language in the config will be used.", diff --git a/locales/es.json b/locales/es.json index fa1d5e48..17cd82b4 100644 --- a/locales/es.json +++ b/locales/es.json @@ -74,6 +74,9 @@ "config.openai_api_key": "", "config.openai_api_model": "", "config.openai_api_root": "", + "config.cohere_api_key": "", + "config.cohere_api_model": "", + "config.cohere_api_root": "", "config.path_matcher": "Igual el path para configuraciones regionales/espacios de nombres personalizados. Comprueba la documentación para más detalles.", "config.preferred_delimiter": "Delimitador preferido para las rutas de acceso compuestas, por defecto \"-\"(guión)", "config.prompt_translating_source": "Solicitud para seleccionar la configuración regional de origen al traducir. Si se establece en false, se utilizará el idioma de origen en la configuración.", diff --git a/locales/fr.json b/locales/fr.json index b7ec6728..20a10a1f 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -74,6 +74,9 @@ "config.openai_api_key": "", "config.openai_api_model": "", "config.openai_api_root": "", + "config.cohere_api_key": "", + "config.cohere_api_model": "", + "config.cohere_api_root": "", "config.path_matcher": "Faire correspondre le chemin pour les locales/namespaces personnalisés. Consultez la documentation pour plus de détails.", "config.preferred_delimiter": "Délimiteur recommandé pour les noms de chemins à trous, par défaut « - » (tiret)", "config.prompt_translating_source": "Invite à sélectionner la langue source à chaque fois. Si défini sur False, le langage du système sera automatiquement utilisé.", diff --git a/locales/hu.json b/locales/hu.json index 677bd2b5..aeb916cd 100644 --- a/locales/hu.json +++ b/locales/hu.json @@ -72,6 +72,9 @@ "config.openai_api_key": "", "config.openai_api_model": "", "config.openai_api_root": "", +"config.cohere_api_key": "", +"config.cohere_api_model": "", +"config.cohere_api_root": "", "config.path_matcher": "A locale / névterekhez tartozó egyezési út. További információért lásd a dokumentációt.", "config.preferred_delimiter": "Az összetett kulcsú útvonalak preferált határolója, alapértelmezetten az - (kötőjel)", "config.prompt_translating_source": "Mindig kérdezze meg forrás nyelvre fordításkor. Ha hamisra van állítva, a konfigurációban megadott forrásnyelv lesz használva.", diff --git a/locales/ja.json b/locales/ja.json index 66486405..309b76e9 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -74,6 +74,9 @@ "config.openai_api_key": "", "config.openai_api_model": "", "config.openai_api_root": "", + "config.cohere_api_key": "", + "config.cohere_api_model": "", + "config.cohere_api_root": "", "config.path_matcher": "", "config.preferred_delimiter": "キーパスを構成するデリミタ。デフォルトは\"-\"(ダッシュ)", "config.prompt_translating_source": "", diff --git a/locales/ko.json b/locales/ko.json index 4c75feb4..339d11cf 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -72,6 +72,9 @@ "config.openai_api_key": "", "config.openai_api_model": "", "config.openai_api_root": "", + "config.cohere_api_key": "", + "config.cohere_api_model": "", + "config.cohere_api_root": "", "config.path_matcher": "사용자 정의 로케일/네임스페이스에 대한 일치 경로. \n자세한 내용은 문서를 확인하세요.", "config.preferred_delimiter": "구성된 키 경로의 기본 구분 기호, 기본값은 \"-\"(대시)", "config.prompt_translating_source": "번역할 때마다 소스 로케일을 선택하라는 메시지가 표시됩니다. \nfalse로 설정하면 구성의 소스 언어가 사용됩니다.", diff --git a/locales/nb-NO.json b/locales/nb-NO.json index 658020bb..936f87a4 100644 --- a/locales/nb-NO.json +++ b/locales/nb-NO.json @@ -74,6 +74,9 @@ "config.openai_api_key": "", "config.openai_api_model": "", "config.openai_api_root": "", + "config.cohere_api_key": "", + "config.cohere_api_model": "", + "config.cohere_api_root": "", "config.path_matcher": "", "config.preferred_delimiter": "Foretrukket skilletegn for komponert nøkkelbane, \"-\"(bindestrek) som standard", "config.prompt_translating_source": "", diff --git a/locales/nl-NL.json b/locales/nl-NL.json index eec160e7..05dce61f 100644 --- a/locales/nl-NL.json +++ b/locales/nl-NL.json @@ -74,6 +74,9 @@ "config.openai_api_key": "", "config.openai_api_model": "", "config.openai_api_root": "", + "config.cohere_api_key": "", + "config.cohere_api_model": "", + "config.cohere_api_root": "", "config.path_matcher": "Pad voor onderscheiding van sleutels op basis van folderstructuur/bestandsnaam. Zie documentatie voor meer uitleg.", "config.preferred_delimiter": "Gewenst scheidingsteken voor sleutel pad, standaard waarde \"-\"(dash)", "config.prompt_translating_source": "Altijd vragen om de bron locale te selectering bij vertalingen. Indien false, dan zal de bron taal in de configuratie gebruikt worden.", diff --git a/locales/pt-BR.json b/locales/pt-BR.json index 207fa8f1..a2170749 100644 --- a/locales/pt-BR.json +++ b/locales/pt-BR.json @@ -74,6 +74,9 @@ "config.openai_api_key": "", "config.openai_api_model": "", "config.openai_api_root": "", + "config.cohere_api_key": "", + "config.cohere_api_model": "", + "config.cohere_api_root": "", "config.path_matcher": "", "config.preferred_delimiter": "Delimitador para o caminho da chave, o padrão é \"-\"(hífen)", "config.prompt_translating_source": "Sempre perguntar idioma de origem durante tradução. Se o valor for \"false\", será usado o idioma de origem das configurações.", diff --git a/locales/ru.json b/locales/ru.json index fb3f39b5..890fc864 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -74,6 +74,9 @@ "config.openai_api_key": "", "config.openai_api_model": "", "config.openai_api_root": "", + "config.cohere_api_key": "", + "config.cohere_api_model": "", + "config.cohere_api_root": "", "config.path_matcher": "Сопоставить путь для пользовательского перевода/пространства имен. Подробности смотрите в документации.", "config.preferred_delimiter": "Желаемый разделитель пути составного ключа, значение по умолчанию \"-\"(тире)", "config.prompt_translating_source": "Предлагать выбрать исходный перевод при переводе каждый раз. Если установлено false, будет использоваться исходный перевод из конфигурации.", diff --git a/locales/sv-SE.json b/locales/sv-SE.json index da7c84e9..990ea30c 100644 --- a/locales/sv-SE.json +++ b/locales/sv-SE.json @@ -74,6 +74,9 @@ "config.openai_api_key": "", "config.openai_api_model": "", "config.openai_api_root": "", + "config.cohere_api_key": "", + "config.cohere_api_model": "", + "config.cohere_api_root": "", "config.path_matcher": "Matcha sökväg för egna locale-filer/namespace. Se dokumentation för mer detaljer.", "config.preferred_delimiter": "Föredragen avskiljare av sammansatta nyckelsökvägar, som standard satt till \"-\" (bindestreck)", "config.prompt_translating_source": "Fråga om att välja locale-filskälla varje gång vid översättning. Om satt till `false` kommer locale-filskällan i konfigurationen användas.", diff --git a/locales/th.json b/locales/th.json index e970fefe..4c55296a 100644 --- a/locales/th.json +++ b/locales/th.json @@ -72,6 +72,9 @@ "config.openai_api_key": "", "config.openai_api_model": "", "config.openai_api_root": "", + "config.cohere_api_key": "", + "config.cohere_api_model": "", + "config.cohere_api_root": "", "config.path_matcher": "จับคู่เส้นทางสำหรับภาษา/namespace ที่กำหนดเอง ตรวจสอบเอกสารสำหรับรายละเอียดเพิ่มเติม", "config.preferred_delimiter": "ตัวคั่นที่ต้องการของเส้นทางที่ประกอบขึ้น ค่าเริ่มต้นคือ \"-\"(เส้นประ)", "config.prompt_translating_source": "พร้อมท์ให้เลือกภาษาต้นทางในการแปลทุกครั้ง หากตั้งค่าเป็นเท็จ ภาษาต้นทางในการกำหนดค่าจะถูกใช้", diff --git a/locales/tr.json b/locales/tr.json index f6d53d7a..4c516177 100644 --- a/locales/tr.json +++ b/locales/tr.json @@ -74,6 +74,9 @@ "config.openai_api_key": "", "config.openai_api_model": "", "config.openai_api_root": "", + "config.cohere_api_key": "", + "config.cohere_api_model": "", + "config.cohere_api_root": "", "config.path_matcher": "Dosya yolunu özelleştirilmiş bir dil/namespace'e göre eşleştirin. Daha fazla ayrıntı için dokümantasyona göz atın.", "config.preferred_delimiter": "Bileşik anahtar yolları için ayıran karakter, varsayılan \"-\" (tire)", "config.prompt_translating_source": "Her seferinde çeviride kaynak dili seçmesini isteyin. Eğer false olarak ayarlıysa, yapılandırmadaki kaynak dil kullanılacaktır.", diff --git a/locales/uk-UA.json b/locales/uk-UA.json index 038f709f..9fb5266a 100644 --- a/locales/uk-UA.json +++ b/locales/uk-UA.json @@ -74,6 +74,9 @@ "config.openai_api_key": "", "config.openai_api_model": "", "config.openai_api_root": "", + "config.cohere_api_key": "", + "config.cohere_api_model": "", + "config.cohere_api_root": "", "config.path_matcher": "Шлях для розрізнення ключів на основі структури папок/імені файлу. Подробиці дивіться в документації.", "config.preferred_delimiter": "Бажаний роздільник шляху складеного ключа, значення за замовчуванням \"-\"(тире)", "config.prompt_translating_source": "Пропонувати обирати мовний стандарт кожен раз при перекладі. Якщо встановлено false, буде використовуватися мова з конфігурації.", diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 8097d376..aa1ee264 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -74,6 +74,9 @@ "config.openai_api_key": "", "config.openai_api_model": "", "config.openai_api_root": "", + "config.cohere_api_key": "", + "config.cohere_api_model": "", + "config.cohere_api_root": "", "config.path_matcher": "匹配自定义区域设置/命名空间的路径。查看文档了解更多详细信息。", "config.preferred_delimiter": "组合键路径的默认分隔符,默认为 \"-\"(短划线)", "config.prompt_translating_source": "每次翻译时都会提示选择源语言环境。\n如果设置为false,将使用配置中的源语言。", diff --git a/locales/zh-TW.json b/locales/zh-TW.json index a1dd5621..88220636 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -74,6 +74,9 @@ "config.openai_api_key": "", "config.openai_api_model": "", "config.openai_api_root": "", + "config.cohere_api_key": "", + "config.cohere_api_model": "", + "config.cohere_api_root": "", "config.path_matcher": "匹配自定義區域設置/命名空間的路徑。\n查看文件了解更多詳細信息。", "config.preferred_delimiter": "組合鍵路徑的首選分隔符,默認為 \"-\"(短劃線)", "config.prompt_translating_source": "每次翻譯時都會提示選擇源語言環境。\n如果設置為false,將使用配置中的源語言。", diff --git a/package.json b/package.json index 154b2439..5189dc62 100644 --- a/package.json +++ b/package.json @@ -1042,7 +1042,8 @@ "deepl", "libretranslate", "baidu", - "openai" + "openai", + "cohere" ] }, "default": [ @@ -1127,6 +1128,26 @@ ], "description": "%config.openai_api_model%" }, + "i18n-ally.translate.cohere.apiKey": { + "type": "string", + "default": null, + "description": "%config.cohere_api_key%" + }, + "i18n-ally.translate.cohere.apiRoot": { + "type": "string", + "default": "https://api.cohere.com", + "description": "%config.cohere_api_root%" + }, + "i18n-ally.translate.cohere.apiModel": { + "type": "string", + "default": "command-r7b-12-2024", + "enum": [ + "command-r7b-12-2024", + "command-r-plus-08-2024", + "command-r-08-2024" + ], + "description": "%config.cohere_api_model%" + }, "i18n-ally.usage.scanningIgnore": { "type": "array", "items": { diff --git a/src/core/Config.ts b/src/core/Config.ts index 3eedd1c5..583a1967 100644 --- a/src/core/Config.ts +++ b/src/core/Config.ts @@ -180,7 +180,7 @@ export class Config { return this.getConfig('sortCompare') || 'binary' } - static get sortLocale(): string | undefined{ + static get sortLocale(): string | undefined { return this.getConfig('sortLocale') } @@ -584,6 +584,18 @@ export class Config { return this.getConfig('translate.openai.apiModel') ?? 'gpt-3.5-turbo' } + static get cohereApiKey() { + return this.getConfig('translate.cohere.apiKey') + } + + static get cohereApiRoot() { + return this.getConfig('translate.cohere.apiRoot') + } + + static get cohereApiModel() { + return this.getConfig('translate.cohere.apiModel') ?? 'command-r7b-12-2024' + } + static get telemetry(): boolean { return workspace.getConfiguration().get('telemetry.enableTelemetry') as boolean } diff --git a/src/translators/engines/cohere.ts b/src/translators/engines/cohere.ts new file mode 100644 index 00000000..eb2e15ad --- /dev/null +++ b/src/translators/engines/cohere.ts @@ -0,0 +1,73 @@ +import axios from 'axios' +import TranslateEngine, { TranslateOptions, TranslateResult } from './base' +import { Config } from '~/core' + +export default class CohereTranslate extends TranslateEngine { + apiRoot = 'https://api.cohere.com' + systemPrompt = `You are a professional translation engine. +Please follow these rules strictly: + +1. Only provide the translation of the text. Do not add any additional information. +2. Do not translate tags, e.g., TEXT_TO_TRANSLATE. +3. Do not translate variable names used for interpolation, e.g., {item}. +4. Do not translate JSON keys, only translate the values. +5. Maintain the original capitalization of the text.` + + async translate(options: TranslateOptions) { + const apiKey = Config.cohereApiKey + let apiRoot = this.apiRoot + if (Config.cohereApiRoot) apiRoot = Config.cohereApiRoot.replace(/\/$/, '') + const model = Config.cohereApiModel + + const response = await axios.post( + `${apiRoot}/v2/chat`, + { + model, + messages: [ + { + role: 'system', + content: this.systemPrompt, + }, + { + role: 'user', + content: this.generateUserPrompts(options), + }, + ], + }, + { + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${apiKey}`, + }, + }, + ) + + return this.transform(response, options) + } + + transform(response: any, options: TranslateOptions): TranslateResult { + const { text, from = 'auto', to = 'auto' } = options + + const translatedText = response.data.message.content[0].text.trim() + + const r: TranslateResult = { + text, + to, + from, + response, + result: translatedText ? [translatedText] : undefined, + linkToResult: '', + } + + return r + } + + generateUserPrompts(options: TranslateOptions): string { + const sourceLang = options.from + const targetLang = options.to + + const generatedUserPrompt = `translate from ${sourceLang} to ${targetLang}:\n\n${options.text}` + + return generatedUserPrompt + } +} diff --git a/src/translators/index.ts b/src/translators/index.ts index 31474df6..eb70b6a0 100644 --- a/src/translators/index.ts +++ b/src/translators/index.ts @@ -5,6 +5,7 @@ import DeepLTranslateEngine from './engines/deepl' import LibreTranslateEngine from './engines/libretranslate' import BaiduTranslate from './engines/baidu' import OpenAITranslateEngine from './engines/openai' +import CohereTranslateEngine from './engines/cohere' export class Translator { engines: Record ={ @@ -14,6 +15,7 @@ export class Translator { 'libretranslate': new LibreTranslateEngine(), 'baidu': new BaiduTranslate(), 'openai': new OpenAITranslateEngine(), + 'cohere': new CohereTranslateEngine(), } async translate(options: TranslateOptions & { engine: string }) { @@ -30,6 +32,7 @@ export { LibreTranslateEngine, BaiduTranslate, OpenAITranslateEngine, + CohereTranslateEngine, } export * from './engines/base'