Skip to content

Commit b3749c8

Browse files
authored
feat: add OpenAI translate engine (#983)
1 parent 03796e4 commit b3749c8

21 files changed

+167
-1
lines changed

locales/de.json

+3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
"config.locale_country_map": "Ein Objekt zur Zuordnung von zwei Buchstaben des Lokalisierungscodes zum Ländercode",
7070
"config.locales_paths": "Pfad des Lokalisierungsordners (relative zum Hauptverzeichnes). Glob pattern akzeptiert.",
7171
"config.namespace": "Namespace aktivieren. Dokumentation für weitere Details prüfen.",
72+
"config.openai_api_key": "",
73+
"config.openai_api_model": "",
74+
"config.openai_api_root": "",
7275
"config.path_matcher": "Pfad für benutzerdefinierten Namespace. Dokumentation für weitere details prüfen.",
7376
"config.preferred_delimiter": "Bevorzugtes Trennzeichen für zusammengesetzte Schlüsse. Standardwert ist \"-\"",
7477
"config.prompt_translating_source": "Aufforderung zur Auswahl der Quellsprache bei jeder Verwendung. Wenn false, die Quellsprache wird aus der Konfiguration gelesen.",

locales/en.json

+3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
"config.locale_country_map": "An object to map two letters locale code to country code",
7070
"config.locales_paths": "Path to locales directory (relative to project root). Glob pattern is also acceptable.",
7171
"config.namespace": "Enable namespaces. Check out the docs for more details.",
72+
"config.openai_api_key": "OpenAI API key",
73+
"config.openai_api_model": "OpenAI chatgpt model",
74+
"config.openai_api_root": "OpenAI API root URL",
7275
"config.path_matcher": "Match path for custom locale/namespace. Check out the docs for more details.",
7376
"config.preferred_delimiter": "Preferred delimiter of composed keypath, default to \"-\"(dash)",
7477
"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.",

locales/es.json

+3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
"config.locale_country_map": "Un objeto para relacionar dos letras de código regional a código de país",
7070
"config.locales_paths": "Ruta de acceso al directorio de configuración regional (relativa a la raíz del proyecto). Acepta patrón Glob.",
7171
"config.namespace": "Habilitar espacios de nombres. Comprueba la documentación para más detalles.",
72+
"config.openai_api_key": "",
73+
"config.openai_api_model": "",
74+
"config.openai_api_root": "",
7275
"config.path_matcher": "Igual el path para configuraciones regionales/espacios de nombres personalizados. Comprueba la documentación para más detalles.",
7376
"config.preferred_delimiter": "Delimitador preferido para las rutas de acceso compuestas, por defecto \"-\"(guión)",
7477
"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.",

locales/fr.json

+3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
"config.locale_country_map": "Objet pour mapper le code local de deux lettres au code du pays",
7070
"config.locales_paths": "Chemin du répertoire de la locale (par rapport à la racine). Les schémas globaux sont acceptés.",
7171
"config.namespace": "Activer les namespaces. Vérifiez la documentation pour plus de détails.",
72+
"config.openai_api_key": "",
73+
"config.openai_api_model": "",
74+
"config.openai_api_root": "",
7275
"config.path_matcher": "Faire correspondre le chemin pour les locales/namespaces personnalisés. Consultez la documentation pour plus de détails.",
7376
"config.preferred_delimiter": "Délimiteur recommandé pour les noms de chemins à trous, par défaut « - » (tiret)",
7477
"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é.",

locales/hu.json

+3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
"config.locale_country_map": "Egy objektum a két karakteres locale-kódokat országkódra képző szolgáltatáshoz.",
7070
"config.locales_paths": "Az elérési utak a locale fájlokhoz (a projekt gyökere relativ elérési útvonala). Globális mintát is elfogad.",
7171
"config.namespace": "Névterek engedélyezése. További információért lásd a dokumentációt.",
72+
"config.openai_api_key": "",
73+
"config.openai_api_model": "",
74+
"config.openai_api_root": "",
7275
"config.path_matcher": "A locale / névterekhez tartozó egyezési út. További információért lásd a dokumentációt.",
7376
"config.preferred_delimiter": "Az összetett kulcsú útvonalak preferált határolója, alapértelmezetten az - (kötőjel)",
7477
"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.",

locales/ja.json

+3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
"config.locale_country_map": "",
7070
"config.locales_paths": "ロケールディレクトリのパス (プロジェクトルートの相対パス), glob pattern が有効です。",
7171
"config.namespace": "",
72+
"config.openai_api_key": "",
73+
"config.openai_api_model": "",
74+
"config.openai_api_root": "",
7275
"config.path_matcher": "",
7376
"config.preferred_delimiter": "キーパスを構成するデリミタ。デフォルトは\"-\"(ダッシュ)",
7477
"config.prompt_translating_source": "",

locales/ko.json

+3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
"config.locale_country_map": "두 글자인 로케일 코드를 국가 코드에 매핑하는 개체",
7070
"config.locales_paths": "로케일 디렉토리 경로(프로젝트 루트 기준). \nGlob 패턴도 허용됩니다.",
7171
"config.namespace": "네임스페이스 활성화. \n자세한 내용은 문서를 확인하세요.",
72+
"config.openai_api_key": "",
73+
"config.openai_api_model": "",
74+
"config.openai_api_root": "",
7275
"config.path_matcher": "사용자 정의 로케일/네임스페이스에 대한 일치 경로. \n자세한 내용은 문서를 확인하세요.",
7376
"config.preferred_delimiter": "구성된 키 경로의 기본 구분 기호, 기본값은 \"-\"(대시)",
7477
"config.prompt_translating_source": "번역할 때마다 소스 로케일을 선택하라는 메시지가 표시됩니다. \nfalse로 설정하면 구성의 소스 언어가 사용됩니다.",

locales/nb-NO.json

+3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
"config.locale_country_map": "",
7070
"config.locales_paths": "Bane til oversettelsesmappe (relativt til prosjektets rotmappe). Glob-mønster er også akseptabelt.",
7171
"config.namespace": "",
72+
"config.openai_api_key": "",
73+
"config.openai_api_model": "",
74+
"config.openai_api_root": "",
7275
"config.path_matcher": "",
7376
"config.preferred_delimiter": "Foretrukket skilletegn for komponert nøkkelbane, \"-\"(bindestrek) som standard",
7477
"config.prompt_translating_source": "",

locales/nl-NL.json

+3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
"config.locale_country_map": "",
7070
"config.locales_paths": "Pad naar locale folder (relatief aan project root), glob patroon is acceptabel.",
7171
"config.namespace": "Schakel in om sleutels te onderscheiden op basis van bestandsnaam. Zie documentatie voor meer uitleg.",
72+
"config.openai_api_key": "",
73+
"config.openai_api_model": "",
74+
"config.openai_api_root": "",
7275
"config.path_matcher": "Pad voor onderscheiding van sleutels op basis van folderstructuur/bestandsnaam. Zie documentatie voor meer uitleg.",
7376
"config.preferred_delimiter": "Gewenst scheidingsteken voor sleutel pad, standaard waarde \"-\"(dash)",
7477
"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.",

locales/pt-BR.json

+3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
"config.locale_country_map": "",
7070
"config.locales_paths": "Caminho para os arquivos de tradução (relativo à raiz do projeto). O padrão glob também é aceito.",
7171
"config.namespace": "",
72+
"config.openai_api_key": "",
73+
"config.openai_api_model": "",
74+
"config.openai_api_root": "",
7275
"config.path_matcher": "",
7376
"config.preferred_delimiter": "Delimitador para o caminho da chave, o padrão é \"-\"(hífen)",
7477
"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.",

locales/ru.json

+3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
"config.locale_country_map": "Объект для сопоставления двухбуквенного кода перевода с кодом страны",
7070
"config.locales_paths": "Путь к каталогу переводов (относительно корня проекта). Допускается шаблон соответствия Glob",
7171
"config.namespace": "Включить пространства имен. Подробности смотрите в документации.",
72+
"config.openai_api_key": "",
73+
"config.openai_api_model": "",
74+
"config.openai_api_root": "",
7275
"config.path_matcher": "Сопоставить путь для пользовательского перевода/пространства имен. Подробности смотрите в документации.",
7376
"config.preferred_delimiter": "Желаемый разделитель пути составного ключа, значение по умолчанию \"-\"(тире)",
7477
"config.prompt_translating_source": "Предлагать выбрать исходный перевод при переводе каждый раз. Если установлено false, будет использоваться исходный перевод из конфигурации.",

locales/sv-SE.json

+3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
"config.locale_country_map": "Ett objekt för att mappa två-bokstäverskod till landskod",
7070
"config.locales_paths": "Sökväg till locale-filer (relativt projektets rot). `glob` mönster är också accepterat.",
7171
"config.namespace": "Aktivera \"namespaces\". Se dokumentation för mer detaljer.",
72+
"config.openai_api_key": "",
73+
"config.openai_api_model": "",
74+
"config.openai_api_root": "",
7275
"config.path_matcher": "Matcha sökväg för egna locale-filer/namespace. Se dokumentation för mer detaljer.",
7376
"config.preferred_delimiter": "Föredragen avskiljare av sammansatta nyckelsökvägar, som standard satt till \"-\" (bindestreck)",
7477
"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.",

locales/th.json

+3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
"config.locale_country_map": "วัตถุที่จะ map รหัสตำแหน่งภาษาตัวอักษรสองตัวกับรหัสประเทศ",
7070
"config.locales_paths": "เส้นทางไปยังไดเร็กทอรีภาษา (สัมพันธ์กับเส้นทางแรกสุดโปรเจ็กต์) รูปแบบ Glob ก็เป็นที่ยอมรับเช่นกัน",
7171
"config.namespace": "เปิดใช้งาน namespace ตรวจสอบเอกสารสำหรับรายละเอียดเพิ่มเติม",
72+
"config.openai_api_key": "",
73+
"config.openai_api_model": "",
74+
"config.openai_api_root": "",
7275
"config.path_matcher": "จับคู่เส้นทางสำหรับภาษา/namespace ที่กำหนดเอง ตรวจสอบเอกสารสำหรับรายละเอียดเพิ่มเติม",
7376
"config.preferred_delimiter": "ตัวคั่นที่ต้องการของเส้นทางที่ประกอบขึ้น ค่าเริ่มต้นคือ \"-\"(เส้นประ)",
7477
"config.prompt_translating_source": "พร้อมท์ให้เลือกภาษาต้นทางในการแปลทุกครั้ง หากตั้งค่าเป็นเท็จ ภาษาต้นทางในการกำหนดค่าจะถูกใช้",

locales/tr.json

+3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
"config.locale_country_map": "İki harfli yerel kodunu ülke koduyla eşleştirmek için bir 'object'",
7070
"config.locales_paths": "Çeviri dosyalarının bulunduğu alan (projenin olduğu yere göre). Global patternler de kabul edilmektedir.",
7171
"config.namespace": "Namespace'lerini etkinleştirin. Daha fazla ayrıntı için dokümantasyona göz atın.",
72+
"config.openai_api_key": "",
73+
"config.openai_api_model": "",
74+
"config.openai_api_root": "",
7275
"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.",
7376
"config.preferred_delimiter": "Bileşik anahtar yolları için ayıran karakter, varsayılan \"-\" (tire)",
7477
"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.",

locales/uk-UA.json

+3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
"config.locale_country_map": "Об'єкт для зіставлення двобуквеного коду мовного стандарту з кодом країни",
7070
"config.locales_paths": "Шлях до каталогу перекладів (щодо кореня проекту). Допускається шаблон відповідності Glob",
7171
"config.namespace": "Включити `namespace`. Подробиці дивіться в документації.",
72+
"config.openai_api_key": "",
73+
"config.openai_api_model": "",
74+
"config.openai_api_root": "",
7275
"config.path_matcher": "Шлях для розрізнення ключів на основі структури папок/імені файлу. Подробиці дивіться в документації.",
7376
"config.preferred_delimiter": "Бажаний роздільник шляху складеного ключа, значення за замовчуванням \"-\"(тире)",
7477
"config.prompt_translating_source": "Пропонувати обирати мовний стандарт кожен раз при перекладі. Якщо встановлено false, буде використовуватися мова з конфігурації.",

locales/zh-CN.json

+3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
"config.locale_country_map": "将两个字母的区域设置代码映射到国家/地区代码的对象",
7070
"config.locales_paths": "翻译文件夹路径 (相对于项目根目录),你也可以使用Glob匹配模式。",
7171
"config.namespace": "启用命名空间。查看文档了解更多详细信息。",
72+
"config.openai_api_key": "",
73+
"config.openai_api_model": "",
74+
"config.openai_api_root": "",
7275
"config.path_matcher": "匹配自定义区域设置/命名空间的路径。查看文档了解更多详细信息。",
7376
"config.preferred_delimiter": "组合键路径的默认分隔符,默认为 \"-\"(短划线)",
7477
"config.prompt_translating_source": "每次翻译时都会提示选择源语言环境。\n如果设置为false,将使用配置中的源语言。",

locales/zh-TW.json

+3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
"config.locale_country_map": "將兩個字母的區域設置代碼映射到國家/地區代碼的對象",
7070
"config.locales_paths": "翻譯資料夾路徑 (相對於專案根目錄),你也可以使用Glob匹配模式。",
7171
"config.namespace": "啟用命名空間。\n查看文件了解更多詳細信息。",
72+
"config.openai_api_key": "",
73+
"config.openai_api_model": "",
74+
"config.openai_api_root": "",
7275
"config.path_matcher": "匹配自定義區域設置/命名空間的路徑。\n查看文件了解更多詳細信息。",
7376
"config.preferred_delimiter": "組合鍵路徑的首選分隔符,默認為 \"-\"(短劃線)",
7477
"config.prompt_translating_source": "每次翻譯時都會提示選擇源語言環境。\n如果設置為false,將使用配置中的源語言。",

package.json

+29-1
Original file line numberDiff line numberDiff line change
@@ -1024,7 +1024,8 @@
10241024
"google",
10251025
"google-cn",
10261026
"deepl",
1027-
"libretranslate"
1027+
"libretranslate",
1028+
"openai"
10281029
]
10291030
},
10301031
"default": [
@@ -1072,6 +1073,33 @@
10721073
"default": "http://localhost:5000",
10731074
"description": "%config.libretranslate_api_root%"
10741075
},
1076+
"i18n-ally.translate.openai.apiKey": {
1077+
"type": "string",
1078+
"default": null,
1079+
"description": "%config.openai_api_key%"
1080+
},
1081+
"i18n-ally.translate.openai.apiRoot": {
1082+
"type": "string",
1083+
"default": "https://api.openai.com",
1084+
"description": "%config.openai_api_root%"
1085+
},
1086+
"i18n-ally.translate.openai.apiModel": {
1087+
"type": "string",
1088+
"default": "gpt-3.5-turbo",
1089+
"enum": [
1090+
"gpt-3.5-turbo",
1091+
"gpt-3.5-turbo-16k",
1092+
"gpt-3.5-turbo-0301",
1093+
"gpt-3.5-turbo-0613",
1094+
"gpt-4",
1095+
"gpt-4-0314",
1096+
"gpt-4-0613",
1097+
"gpt-4-32k",
1098+
"gpt-4-32k-0314",
1099+
"gpt-4-32k-0613"
1100+
],
1101+
"description": "%config.openai_api_model%"
1102+
},
10751103
"i18n-ally.usage.scanningIgnore": {
10761104
"type": "array",
10771105
"items": {

src/core/Config.ts

+12
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,18 @@ export class Config {
556556
return this.getConfig<string | null | undefined>('translate.libre.apiRoot')
557557
}
558558

559+
static get openaiApiKey() {
560+
return this.getConfig<string | null | undefined>('translate.openai.apiKey')
561+
}
562+
563+
static get openaiApiRoot() {
564+
return this.getConfig<string | null | undefined>('translate.openai.apiRoot')
565+
}
566+
567+
static get openaiApiModel() {
568+
return this.getConfig<string>('translate.openai.apiModel') ?? 'gpt-3.5-turbo'
569+
}
570+
559571
static get telemetry(): boolean {
560572
return workspace.getConfiguration().get('telemetry.enableTelemetry') as boolean
561573
}

src/translators/engines/openai.ts

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import axios from "axios";
2+
import TranslateEngine, { TranslateOptions, TranslateResult } from "./base";
3+
import { Config } from "~/core";
4+
5+
export default class OpenAITranslate extends TranslateEngine {
6+
apiRoot = "https://api.openai.com";
7+
systemPrompt = "You are a professional translation engine. Please translate text without explanation.";
8+
9+
async translate(options: TranslateOptions) {
10+
let apiKey = Config.openaiApiKey;
11+
let apiRoot = this.apiRoot;
12+
if (Config.openaiApiRoot) apiRoot = Config.openaiApiRoot.replace(/\/$/, "");
13+
let model = Config.openaiApiModel;
14+
15+
const response = await axios.post(
16+
`${apiRoot}/v1/chat/completions`,
17+
{
18+
model,
19+
temperature: 0,
20+
max_tokens: 1000,
21+
top_p: 1,
22+
frequency_penalty: 1,
23+
presence_penalty: 1,
24+
messages: [
25+
{
26+
role: "system",
27+
content: this.systemPrompt,
28+
},
29+
{
30+
role: "user",
31+
content: this.generateUserPrompts(options),
32+
},
33+
],
34+
},
35+
{
36+
headers: {
37+
"Content-Type": "application/json",
38+
Authorization: `Bearer ${apiKey}`,
39+
},
40+
}
41+
);
42+
43+
return this.transform(response, options);
44+
}
45+
46+
transform(response: any, options: TranslateOptions): TranslateResult {
47+
const { text, from = "auto", to = "auto" } = options;
48+
49+
const translatedText = response.data.choices[0].message.content?.trim();
50+
51+
const r: TranslateResult = {
52+
text,
53+
to,
54+
from,
55+
response,
56+
result: translatedText ? [translatedText] : undefined,
57+
linkToResult: "",
58+
};
59+
60+
61+
return r;
62+
}
63+
64+
generateUserPrompts(options: TranslateOptions): string {
65+
const sourceLang = options.from;
66+
const targetLang = options.to;
67+
68+
let generatedUserPrompt = `translate from ${sourceLang} to ${targetLang}:\n\n${options.text}`;
69+
70+
return generatedUserPrompt;
71+
}
72+
}

src/translators/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ import GoogleTranslateEngine from './engines/google'
33
import GoogleTranslateCnEngine from './engines/google-cn'
44
import DeepLTranslateEngine from './engines/deepl'
55
import LibreTranslateEngine from './engines/libretranslate'
6+
import OpenAITranslateEngine from './engines/openai'
67

78
export class Translator {
89
engines: Record<string, TranslateEngine> ={
910
'google': new GoogleTranslateEngine(),
1011
'google-cn': new GoogleTranslateCnEngine(),
1112
'deepl': new DeepLTranslateEngine(),
1213
'libretranslate': new LibreTranslateEngine(),
14+
'openai': new OpenAITranslateEngine(),
1315
}
1416

1517
async translate(options: TranslateOptions & { engine: string }) {
@@ -24,6 +26,7 @@ export {
2426
GoogleTranslateCnEngine,
2527
DeepLTranslateEngine,
2628
LibreTranslateEngine,
29+
OpenAITranslateEngine,
2730
}
2831

2932
export * from './engines/base'

0 commit comments

Comments
 (0)