gpt-po 1.1.2 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- package/.github/FUNDING.yml +1 -0
- package/CHANGELOG.md +17 -0
- package/README.md +13 -3
- package/README_zh-CN.md +13 -3
- package/lib/package.json +1 -1
- package/lib/src/index.d.ts +1 -1
- package/lib/src/index.js +9 -24
- package/lib/src/systemprompt.txt +1 -31
- package/lib/src/translate.d.ts +1 -1
- package/lib/src/translate.js +101 -63
- package/lib/src/userprompt.txt +27 -0
- package/lib/src/utils.d.ts +6 -1
- package/lib/src/utils.js +6 -6
- package/package.json +1 -1
@@ -0,0 +1 @@
|
|
1
|
+
buy_me_a_coffee: ryanhex
|
package/CHANGELOG.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## [1.2.0] - 2024-11-26
|
4
|
+
- Submit multiple entries per chat to boost speed and reduce tokens
|
5
|
+
- Command `systemprompt` has been removed
|
6
|
+
- Bug fixes
|
7
|
+
|
8
|
+
## [1.1.2] - 2024-11-12
|
9
|
+
- Update command options for OpenAI model
|
10
|
+
- Dependency updates, target es2022, module(Nodejs 18+)
|
11
|
+
|
12
|
+
## [1.0.11] - 2023-10-26
|
13
|
+
- Add `gpt-po remove` command
|
14
|
+
- improve prompt for translation
|
15
|
+
|
16
|
+
## [1.0.9] - 2023-09-08
|
17
|
+
- Can add dictionaries for each languages
|
package/README.md
CHANGED
@@ -7,6 +7,8 @@ Translation tool for gettext (po) files that supports custom system prompts and
|
|
7
7
|
|
8
8
|
Read in other languages: English | [简体中文](./README_zh-CN.md)
|
9
9
|
|
10
|
+
<a href="https://buymeacoffee.com/ryanhex" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-red.png" alt="Buy Me A Coffee" height="41" width="174"></a>
|
11
|
+
|
10
12
|
## Installation
|
11
13
|
|
12
14
|
```
|
@@ -25,8 +27,6 @@ Set `OPENAI_API_KEY` before using this tool.
|
|
25
27
|
- `gpt-po --dir .` Translate all po files in current directory to a designated target language.
|
26
28
|
- `gpt-po userdict` Modify or view user dictionaries
|
27
29
|
- `gpt-po userdict --explore` Explore user dictionaries, if you want add new dictionaries or modify existing dictionaries. dictionaries can be named as `dictionary-<lang>.json`, for example, `dictionary-zh.json` is the dictionary for Simplified Chinese.
|
28
|
-
- `gpt-po systemprompt` Modify or view system prompts, if you are not sure how to use it, you can leave it alone
|
29
|
-
- `gpt-po systemprompt --reset` Reset system prompts
|
30
30
|
|
31
31
|
```
|
32
32
|
Usage: gpt-po [options] [command]
|
@@ -40,7 +40,6 @@ Options:
|
|
40
40
|
Commands:
|
41
41
|
translate [options] translate po file (default command)
|
42
42
|
sync [options] update po from pot file
|
43
|
-
systemprompt [options] open/edit system prompt
|
44
43
|
userdict [options] open/edit user dictionary
|
45
44
|
remove [options] remove po entries by options
|
46
45
|
help [command] display help for command
|
@@ -81,3 +80,14 @@ Options:
|
|
81
80
|
-rc, --reference-contains <text> remove entries whose reference contains text, text can be a regular expression like /text/ig
|
82
81
|
-h, --help display help for command
|
83
82
|
```
|
83
|
+
|
84
|
+
```
|
85
|
+
Usage: gpt-po sync [options]
|
86
|
+
|
87
|
+
update po from pot file
|
88
|
+
|
89
|
+
Options:
|
90
|
+
--po <file> po file path
|
91
|
+
--pot <file> pot file path
|
92
|
+
-h, --help display help for command
|
93
|
+
```
|
package/README_zh-CN.md
CHANGED
@@ -7,6 +7,8 @@ gettext(po)文件翻译工具,支持自定义系统提示词和用户字典,
|
|
7
7
|
|
8
8
|
使用其他语言阅读:[English](./README.md) | 简体中文
|
9
9
|
|
10
|
+
<a href="https://buymeacoffee.com/ryanhex" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-red.png" alt="请我喝杯咖啡" height="41" width="174"></a>
|
11
|
+
|
10
12
|
## 安装
|
11
13
|
|
12
14
|
```
|
@@ -27,8 +29,6 @@ npm install gpt-po
|
|
27
29
|
- `gpt-po --dir .` 将当前目录下的所有 po 文件翻译成目标语言。
|
28
30
|
- `gpt-po userdict` 修改或查看用户词典。
|
29
31
|
- `gpt-po userdict --explore` 浏览用户词典,如果您想添加新词典或修改现有词典,词典可以命名为 `dictionary-<lang>.json`,例如 `dictionary-zh.json` 是简体中文词典。
|
30
|
-
- `gpt-po systemprompt` 修改或查看系统提示,如果您不确定如何使用它,可以忽略。
|
31
|
-
- `gpt-po systemprompt --reset` 重置系统提示。
|
32
32
|
|
33
33
|
```
|
34
34
|
用法: gpt-po [options] [command]
|
@@ -42,7 +42,6 @@ npm install gpt-po
|
|
42
42
|
命令:
|
43
43
|
translate [options] 翻译 po 文件(默认命令)
|
44
44
|
sync [options] 根据 pot 文件更新 po 文件
|
45
|
-
systemprompt [options] 打开/编辑系统提示
|
46
45
|
userdict [options] 打开/编辑用户词典
|
47
46
|
remove [options] 通过选项删除 po 条目
|
48
47
|
help [command] 显示命令帮助
|
@@ -83,3 +82,14 @@ npm install gpt-po
|
|
83
82
|
-rc, --reference-contains <text> 删除引用包含文本的条目,文本可以是正则表达式,例如 /text/ig
|
84
83
|
-h, --help 显示命令帮助
|
85
84
|
```
|
85
|
+
|
86
|
+
```
|
87
|
+
用法: gpt-po sync [options]
|
88
|
+
|
89
|
+
从 pot 文件中更新条目到 po 文件
|
90
|
+
|
91
|
+
Options:
|
92
|
+
--po <file> po 文件路径
|
93
|
+
--pot <file> pot 文件路径
|
94
|
+
-h, --help 显示命令帮助
|
95
|
+
```
|
package/lib/package.json
CHANGED
package/lib/src/index.d.ts
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
#!/usr/bin/env node
|
1
|
+
#!/usr/bin/env node --no-warnings=ExperimentalWarning
|
2
2
|
export {};
|
package/lib/src/index.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#!/usr/bin/env node
|
1
|
+
#!/usr/bin/env node --no-warnings=ExperimentalWarning
|
2
2
|
import { Command, Option } from "commander";
|
3
3
|
import path from "path";
|
4
4
|
import { fileURLToPath } from "url";
|
@@ -16,7 +16,7 @@ program
|
|
16
16
|
.description("translate po file (default command)")
|
17
17
|
.addOption(new Option("-k, --key <key>", "openai api key").env("OPENAI_API_KEY"))
|
18
18
|
.addOption(new Option("--host <host>", "openai api host").env("OPENAI_API_HOST"))
|
19
|
-
.addOption(new Option("--model <model>", "openai model").env("OPENAI_MODEL").default("gpt-4o"))
|
19
|
+
.addOption(new Option("--model <model>", "openai model").env("OPENAI_MODEL").default("gpt-4o-mini"))
|
20
20
|
.addOption(new Option("--po <file>", "po file path").conflicts("dir"))
|
21
21
|
.addOption(new Option("--dir <dir>", "po file directory").conflicts("po"))
|
22
22
|
.option("-src, --source <lang>", "source language (ISO 639-1)", "en")
|
@@ -57,39 +57,24 @@ program
|
|
57
57
|
.action(async ({ po, pot }) => {
|
58
58
|
await sync(po, pot);
|
59
59
|
});
|
60
|
-
// program command `systemprompt` with help text `open/edit system prompt`
|
61
|
-
program
|
62
|
-
.command("systemprompt")
|
63
|
-
.description("open/edit system prompt")
|
64
|
-
.option("--reset", "reset system prompt to default")
|
65
|
-
.action((args) => {
|
66
|
-
const { reset } = args;
|
67
|
-
// open `systemprompt.txt` file by system text default editor
|
68
|
-
const copyFile = __dirname + "/systemprompt.txt";
|
69
|
-
// user home path
|
70
|
-
const promptFile = findConfig("systemprompt.txt");
|
71
|
-
copyFileIfNotExists(promptFile, copyFile, reset);
|
72
|
-
if (reset) {
|
73
|
-
console.log("systemprompt.txt reset to default");
|
74
|
-
}
|
75
|
-
openFileByDefault(promptFile);
|
76
|
-
});
|
77
60
|
// program command `userdict` with help text `open/edit user dictionary`
|
78
61
|
program
|
79
62
|
.command("userdict")
|
80
63
|
.description("open/edit user dictionary")
|
81
64
|
.option("--explore", "open user dictionary directory")
|
65
|
+
.option("-l, --lang <lang>", "target language (ISO 639-1)")
|
82
66
|
.action((args) => {
|
83
|
-
const { explore } = args;
|
67
|
+
const { explore, lang } = args;
|
84
68
|
// open `dictionary.json` file by system text default editor
|
85
69
|
const copyFile = __dirname + "/dictionary.json";
|
86
|
-
// user home path
|
87
|
-
const dictFile = findConfig("
|
70
|
+
// find from user home path
|
71
|
+
const dictFile = findConfig(`dictionary${lang ? "-" + lang : ""}.json`);
|
88
72
|
if (explore) {
|
89
73
|
// open user dictionary directory
|
90
74
|
return openFileExplorer(dictFile);
|
91
75
|
}
|
92
|
-
|
76
|
+
if (!lang)
|
77
|
+
copyFileIfNotExists(dictFile, copyFile);
|
93
78
|
openFileByDefault(dictFile);
|
94
79
|
});
|
95
80
|
// program command `remove` with help text `remove po entries by options`
|
@@ -113,7 +98,7 @@ program
|
|
113
98
|
translated,
|
114
99
|
translatedNotFuzzy,
|
115
100
|
fuzzyTranslated,
|
116
|
-
referenceContains
|
101
|
+
referenceContains
|
117
102
|
};
|
118
103
|
const output = args.output || po;
|
119
104
|
const translations = await parsePo(po);
|
package/lib/src/systemprompt.txt
CHANGED
@@ -1,31 +1 @@
|
|
1
|
-
You are a language translation expert. You will translate
|
2
|
-
|
3
|
-
1. **Language Codes**:
|
4
|
-
- If provided with an ISO 639 two-letter language code (e.g., `de`), use the language’s main dialect (e.g., `de` is equivalent to `de_DE`).
|
5
|
-
- If provided with both an ISO 639 language code and an ISO 3166 country code (e.g., `es_MX`), translate into that specific regional dialect.
|
6
|
-
|
7
|
-
2. **Placeholder Handling**:
|
8
|
-
- Maintain the positions of placeholders (e.g., %s, %d, {example}) in the translated text. Do not translate placeholders.
|
9
|
-
|
10
|
-
3. **Formatting**:
|
11
|
-
- Preserve the formatting of untranslatable portions.
|
12
|
-
- Retain any whitespace at the beginning or end of the message.
|
13
|
-
- Add or omit a period (.) at the end of your translation to match the incoming message.
|
14
|
-
|
15
|
-
4. **XML Tags**:
|
16
|
-
- Input messages will be wrapped in `<translate>` XML tags.
|
17
|
-
- Respond with the translated message wrapped in `<translated>` XML tags.
|
18
|
-
|
19
|
-
5. **Quality**:
|
20
|
-
- Translate in a colloquial, professional, and elegant manner without sounding like a machine translation.
|
21
|
-
|
22
|
-
6. **Error Handling**:
|
23
|
-
- If you cannot reliably translate a message, respond without `<translated>` XML tags and provide a short reason why.
|
24
|
-
|
25
|
-
**Examples**:
|
26
|
-
- Input: `<translate>This is a message. </translate>`
|
27
|
-
- Output: `<translated>Este es un mensaje. </translated>`
|
28
|
-
- Input: `<translate> Hello %s</translate>`
|
29
|
-
- Output: `<translated> Hola %s</translated>`
|
30
|
-
|
31
|
-
Do not answer questions or explain concepts. Only provide translations within `<translated>` XML tags unless you need to respond with a short error reason.
|
1
|
+
You are a language translation expert. You will carefully follow the translation guidelines to translate the incoming XML messages from one language to another.
|
package/lib/src/translate.d.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { GetTextTranslation } from "gettext-parser";
|
2
2
|
import { OpenAI } from "openai";
|
3
3
|
export declare function init(force?: boolean): OpenAI;
|
4
|
-
export declare function translate(
|
4
|
+
export declare function translate(src: string, lang: string, model: string, translations: GetTextTranslation[], contextFile: string): Promise<void>;
|
5
5
|
export declare function translatePo(model: string, po: string, source: string, lang: string, verbose: boolean, output: string, contextFile: string): Promise<void>;
|
6
6
|
export declare function translatePoDir(model: string | undefined, dir: string, source: string, lang: string, verbose: boolean, contextFile: string): Promise<void>;
|
package/lib/src/translate.js
CHANGED
@@ -8,47 +8,59 @@ const __filename = fileURLToPath(import.meta.url);
|
|
8
8
|
const __dirname = path.dirname(__filename);
|
9
9
|
let _openai;
|
10
10
|
let _systemprompt;
|
11
|
+
let _userprompt;
|
11
12
|
let _userdict;
|
12
13
|
export function init(force) {
|
13
14
|
if (!_openai || force) {
|
14
15
|
let configuration = {
|
15
|
-
apiKey: process.env.OPENAI_API_KEY
|
16
|
+
apiKey: process.env.OPENAI_API_KEY
|
16
17
|
};
|
17
18
|
_openai = new OpenAI(configuration);
|
18
19
|
if (process.env.OPENAI_API_HOST) {
|
19
20
|
_openai.baseURL = process.env.OPENAI_API_HOST.replace(/\/+$/, "") + "/v1";
|
20
21
|
}
|
21
22
|
}
|
22
|
-
// load systemprompt.txt from
|
23
|
+
// load systemprompt.txt from project
|
23
24
|
if (!_systemprompt || force) {
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
_systemprompt = fs.readFileSync(path.join(__dirname, "systemprompt.txt"), "utf-8");
|
26
|
+
}
|
27
|
+
// load userprompt.txt from project
|
28
|
+
if (!_userprompt || force) {
|
29
|
+
_userprompt = fs.readFileSync(path.join(__dirname, "userprompt.txt"), "utf-8");
|
27
30
|
}
|
28
31
|
// load dictionary.json from homedir
|
29
32
|
if (!_userdict || force) {
|
30
33
|
const userdict = findConfig("dictionary.json");
|
31
34
|
copyFileIfNotExists(userdict, path.join(__dirname, "dictionary.json"));
|
32
|
-
_userdict = {
|
35
|
+
_userdict = { default: JSON.parse(fs.readFileSync(userdict, "utf-8")) };
|
33
36
|
}
|
34
37
|
return _openai;
|
35
38
|
}
|
36
|
-
export function translate(
|
37
|
-
const lang_code = lang
|
38
|
-
|
39
|
-
.
|
40
|
-
.
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
39
|
+
export async function translate(src, lang, model, translations, contextFile) {
|
40
|
+
const lang_code = lang
|
41
|
+
.toLowerCase()
|
42
|
+
.trim()
|
43
|
+
.replace(/[\W_]+/g, "-");
|
44
|
+
const dicts = Object.entries(_userdict[lang_code] || _userdict["default"]).reduce((acc, [k, v], idx) => {
|
45
|
+
if (translations.some((tr) => tr.msgid.toLowerCase().includes(k.toLowerCase()))) {
|
46
|
+
acc.user.push(`<translate index="${idx + 1}">${k}</translate>`);
|
47
|
+
acc.assistant.push(`<translated index="${idx + 1}">${v}</translated>`);
|
48
|
+
}
|
49
|
+
return acc;
|
50
|
+
}, { user: [], assistant: [] });
|
51
|
+
const notes = translations
|
52
|
+
.reduce((acc, tr) => {
|
53
|
+
if (tr.comments?.extracted) {
|
54
|
+
acc.push(tr.comments?.extracted);
|
55
|
+
}
|
56
|
+
return acc;
|
57
|
+
}, [])
|
58
|
+
.join("\n");
|
59
|
+
const context = contextFile ? "\n\nContext: " + fs.readFileSync(contextFile, "utf-8") : "";
|
60
|
+
const translationsContent = translations
|
61
|
+
.map((tr, idx) => `<translate index="${idx + dicts.user.length + 1}">${tr.msgid}</translate>`)
|
62
|
+
.join("\n");
|
63
|
+
const res = await _openai.chat.completions.create({
|
52
64
|
model: model,
|
53
65
|
temperature: 0.1,
|
54
66
|
messages: [
|
@@ -58,21 +70,41 @@ export function translate(text, src, lang, model, comments, contextFile) {
|
|
58
70
|
},
|
59
71
|
{
|
60
72
|
role: "user",
|
61
|
-
content:
|
73
|
+
content: `${_userprompt}\n\nWait for my incoming message in "${src}" and translate it into "${lang}"(a language code and an optional region code). ` +
|
74
|
+
notes
|
62
75
|
},
|
63
76
|
{
|
64
77
|
role: "assistant",
|
65
|
-
content: `Understood, I will translate your incoming "${src}" message into "${lang}", carefully following
|
78
|
+
content: `Understood, I will translate your incoming "${src}" message into "${lang}", carefully following guidelines. Please go ahead and send your message for translation.`
|
66
79
|
},
|
67
|
-
// add userdict
|
68
|
-
...dicts
|
80
|
+
// add userdict
|
81
|
+
...(dicts.user.length > 0
|
82
|
+
? [
|
83
|
+
{ role: "user", content: dicts.user.join("\n") },
|
84
|
+
{ role: "assistant", content: dicts.assistant.join("\n") }
|
85
|
+
]
|
86
|
+
: []),
|
87
|
+
// add user translations
|
69
88
|
{
|
70
89
|
role: "user",
|
71
|
-
content:
|
72
|
-
}
|
73
|
-
]
|
90
|
+
content: translationsContent
|
91
|
+
}
|
92
|
+
]
|
74
93
|
}, {
|
75
94
|
timeout: 20000,
|
95
|
+
stream: false
|
96
|
+
});
|
97
|
+
const content = res.choices[0].message.content ?? "";
|
98
|
+
translations.forEach((trans, idx) => {
|
99
|
+
const tag = `<translated index="${idx + dicts.user.length + 1}">`;
|
100
|
+
const s = content.indexOf(tag);
|
101
|
+
if (s > -1) {
|
102
|
+
const e = content.indexOf("</translated>", s);
|
103
|
+
trans.msgstr[0] = content.slice(s + tag.length, e);
|
104
|
+
}
|
105
|
+
else {
|
106
|
+
console.error("Error: Unable to find translation for string [" + trans.msgid + "]");
|
107
|
+
}
|
76
108
|
});
|
77
109
|
}
|
78
110
|
export async function translatePo(model, po, source, lang, verbose, output, contextFile) {
|
@@ -84,7 +116,10 @@ export async function translatePo(model, po, source, lang, verbose, output, cont
|
|
84
116
|
return;
|
85
117
|
}
|
86
118
|
// try to load dictionary by lang-code if it not loaded
|
87
|
-
const lang_code = lang
|
119
|
+
const lang_code = lang
|
120
|
+
.toLowerCase()
|
121
|
+
.trim()
|
122
|
+
.replace(/[\W_]+/g, "-");
|
88
123
|
if (!_userdict[lang_code]) {
|
89
124
|
const lang_dic_file = findConfig(`dictionary-${lang_code}.json`);
|
90
125
|
if (fs.existsSync(lang_dic_file)) {
|
@@ -117,9 +152,9 @@ export async function translatePo(model, po, source, lang, verbose, output, cont
|
|
117
152
|
return;
|
118
153
|
}
|
119
154
|
potrans.headers["Last-Translator"] = `gpt-po v${pkg.version}`;
|
155
|
+
const translations = [];
|
120
156
|
let err429 = false;
|
121
|
-
let
|
122
|
-
for (let i = 0; i < list.length; i++) {
|
157
|
+
for (let i = 0, c = 0; i < list.length; i++) {
|
123
158
|
if (i == 0)
|
124
159
|
printProgress(i, list.length);
|
125
160
|
if (err429) {
|
@@ -127,40 +162,43 @@ export async function translatePo(model, po, source, lang, verbose, output, cont
|
|
127
162
|
await new Promise((resolve) => setTimeout(resolve, 20000));
|
128
163
|
}
|
129
164
|
const trans = list[i];
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
if (!translated.startsWith('<translated>') && !translated.endsWith('</translated>')) {
|
134
|
-
// We got an error response
|
135
|
-
console.log("Error: Unable to translate string [" + trans.msgid + "]. Bot says [" + translated + "]");
|
136
|
-
continue;
|
137
|
-
}
|
138
|
-
// We got a valid translation response
|
139
|
-
trans.msgstr[0] = translated.replace('<translated>', '').replace('</translated>', '');
|
140
|
-
modified = true;
|
141
|
-
if (verbose) {
|
142
|
-
console.log(trans.msgid);
|
143
|
-
console.log(trans.msgstr[0]);
|
144
|
-
}
|
145
|
-
printProgress(i + 1, list.length);
|
146
|
-
await compilePo(potrans, output || po);
|
165
|
+
if (c < 2000) {
|
166
|
+
translations.push(trans);
|
167
|
+
c += trans.msgid.length;
|
147
168
|
}
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
console.error(error.response.status);
|
157
|
-
console.log(error.response.data);
|
169
|
+
if (c >= 2000 || i == list.length - 1) {
|
170
|
+
try {
|
171
|
+
await translate(source, lang, model, translations, contextFile);
|
172
|
+
if (verbose) {
|
173
|
+
translations.forEach((trans) => {
|
174
|
+
console.log(trans.msgid);
|
175
|
+
console.log(trans.msgstr[0]);
|
176
|
+
});
|
158
177
|
}
|
178
|
+
translations.length = 0;
|
179
|
+
c = 0;
|
180
|
+
// update progress
|
181
|
+
printProgress(i + 1, list.length);
|
182
|
+
// save po file after each 2000 characters
|
183
|
+
await compilePo(potrans, output || po);
|
159
184
|
}
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
185
|
+
catch (error) {
|
186
|
+
if (error.response) {
|
187
|
+
if (error.response.status == 429) {
|
188
|
+
// caused by rate limit exceeded, should sleep for 20 seconds.
|
189
|
+
err429 = true;
|
190
|
+
--i;
|
191
|
+
}
|
192
|
+
else {
|
193
|
+
console.error(error.response.status);
|
194
|
+
console.log(error.response.data);
|
195
|
+
}
|
196
|
+
}
|
197
|
+
else {
|
198
|
+
console.error(error.message);
|
199
|
+
if (error.code == "ECONNABORTED") {
|
200
|
+
console.log('you may need to set "HTTPS_PROXY" to reach openai api.');
|
201
|
+
}
|
164
202
|
}
|
165
203
|
}
|
166
204
|
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Translation guidelines are as follows:
|
2
|
+
|
3
|
+
1. **Placeholder Handling**:
|
4
|
+
- Maintain the positions of placeholders (e.g., %s, %d, {example}) in the translated text. Do not translate placeholders.
|
5
|
+
|
6
|
+
2. **Formatting**:
|
7
|
+
- Preserve the formatting of untranslatable portions.
|
8
|
+
- Retain any whitespace at the beginning or end of the message.
|
9
|
+
- Add or omit a period (.) at the end of your translation to match the incoming message.
|
10
|
+
|
11
|
+
3. **XML Tags and Indexing**:
|
12
|
+
- Input messages will be wrapped in `<translate>` XML tags with an index attribute, e.g., `<translate index="1">`.
|
13
|
+
- Respond with the translated message wrapped in `<translated>` XML tags, including the same index attribute, e.g., `<translated index="1">`.
|
14
|
+
|
15
|
+
4. **Multiple Translations**:
|
16
|
+
- You may receive multiple translation requests in a single input, each with a unique index.
|
17
|
+
- Ensuring the complete number of requests are translated, even if they are repeated, while maintaining the original order when possible.
|
18
|
+
|
19
|
+
**Examples**:
|
20
|
+
- Input:
|
21
|
+
`<translate index="1">This is a message. </translate>`
|
22
|
+
`<translate index="2"> Hello %s</translate>`
|
23
|
+
- Output:
|
24
|
+
`<translated index="1">这是一条信息</translated>`
|
25
|
+
`<translated index="2"> 你好 %s</translated>`
|
26
|
+
|
27
|
+
Do not answer questions or explain concepts. Only provide translations within `<translated>` XML tags unless you need to respond with a short error reason.
|
package/lib/src/utils.d.ts
CHANGED
@@ -8,7 +8,12 @@ import { GetTextTranslations } from "gettext-parser";
|
|
8
8
|
export declare function copyFileIfNotExists(file: string, copyFile: string, force?: boolean): void;
|
9
9
|
export declare function openFileByDefault(filePath: string): void;
|
10
10
|
export declare function parsePo(poFile: string, defaultCharset?: string): Promise<GetTextTranslations>;
|
11
|
-
export
|
11
|
+
export type compileOptions = {
|
12
|
+
foldLength?: number;
|
13
|
+
sort?: boolean | ((a: never, b: never) => number);
|
14
|
+
escapeCharacters?: boolean;
|
15
|
+
};
|
16
|
+
export declare function compilePo(data: GetTextTranslations, poFile: string, options?: compileOptions): Promise<void>;
|
12
17
|
export declare function printProgress(progress: number, total: number, extra?: string): void;
|
13
18
|
export declare function gitRootDir(dir?: string): string | null;
|
14
19
|
/**
|
package/lib/src/utils.js
CHANGED
@@ -36,13 +36,13 @@ export function parsePo(poFile, defaultCharset) {
|
|
36
36
|
fs.readFile(poFile, (err, buffer) => {
|
37
37
|
if (err)
|
38
38
|
reject(err);
|
39
|
-
|
39
|
+
const result = po.parse(buffer, defaultCharset ?? "utf-8");
|
40
40
|
resolve(result);
|
41
41
|
});
|
42
42
|
});
|
43
43
|
}
|
44
|
-
export function compilePo(data, poFile) {
|
45
|
-
const buffer = po.compile(data,
|
44
|
+
export function compilePo(data, poFile, options = { foldLength: 120, sort: false, escapeCharacters: true }) {
|
45
|
+
const buffer = po.compile(data, options);
|
46
46
|
return new Promise((resolve, reject) => {
|
47
47
|
fs.writeFile(poFile, buffer, (err) => {
|
48
48
|
if (err)
|
@@ -95,7 +95,7 @@ export function findConfig(fileName) {
|
|
95
95
|
path.join(currentDir, ".gpt-po", fileName),
|
96
96
|
path.join(currentDir, fileName),
|
97
97
|
path.join(gitDir, ".gpt-po", fileName),
|
98
|
-
path.join(homeDir, ".gpt-po", fileName)
|
98
|
+
path.join(homeDir, ".gpt-po", fileName),
|
99
99
|
];
|
100
100
|
// check if file exists and return the first one
|
101
101
|
for (const filePath of filePaths) {
|
@@ -111,10 +111,10 @@ export function findConfig(fileName) {
|
|
111
111
|
* @param location folder or file path
|
112
112
|
*/
|
113
113
|
export function openFileExplorer(location) {
|
114
|
-
if (platform() ===
|
114
|
+
if (platform() === "win32") {
|
115
115
|
exec(`explorer.exe "${path.dirname(location)}"`);
|
116
116
|
}
|
117
|
-
else if (platform() ===
|
117
|
+
else if (platform() === "darwin") {
|
118
118
|
exec(`open "${path.dirname(location)}"`);
|
119
119
|
}
|
120
120
|
else {
|