gpt-po 1.1.1 → 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 +14 -5
- package/README_zh-CN.md +14 -4
- package/lib/package.json +13 -12
- package/lib/src/index.d.ts +1 -1
- package/lib/src/index.js +38 -71
- package/lib/src/manipulate.js +12 -17
- package/lib/src/sync.js +14 -29
- package/lib/src/systemprompt.txt +1 -19
- package/lib/src/translate.d.ts +3 -3
- package/lib/src/translate.js +152 -132
- package/lib/src/userprompt.txt +27 -0
- package/lib/src/utils.d.ts +6 -1
- package/lib/src/utils.js +23 -34
- package/package.json +13 -12
- /package/lib/src/{dictionary-template.json → dictionary.json} +0 -0
@@ -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
|
@@ -54,8 +53,7 @@ translate po file (default command)
|
|
54
53
|
Options:
|
55
54
|
-k, --key <key> openai api key (env: OPENAI_API_KEY)
|
56
55
|
--host <host> openai api host (env: OPENAI_API_HOST)
|
57
|
-
--model <model> openai model (
|
58
|
-
default: "gpt-4o")
|
56
|
+
--model <model> openai model (default: "gpt-4o-mini", env: OPENAI_MODEL)
|
59
57
|
--po <file> po file path
|
60
58
|
--dir <dir> po file directory
|
61
59
|
-src, --source <lang> source language (default: "english")
|
@@ -82,3 +80,14 @@ Options:
|
|
82
80
|
-rc, --reference-contains <text> remove entries whose reference contains text, text can be a regular expression like /text/ig
|
83
81
|
-h, --help display help for command
|
84
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] 显示命令帮助
|
@@ -56,7 +55,7 @@ npm install gpt-po
|
|
56
55
|
选项:
|
57
56
|
-k, --key <key> openai api key (环境变量: OPENAI_API_KEY)
|
58
57
|
--host <host> openai api host (环境变量: OPENAI_API_HOST)
|
59
|
-
--model <model> openai 模型 (
|
58
|
+
--model <model> openai 模型 (默认: "gpt-4o-mini", 环境变量: OPENAI_MODEL)
|
60
59
|
--po <file> po 文件路径
|
61
60
|
--dir <dir> po 文件目录
|
62
61
|
-src, --source <lang> 源语言 (默认: "english")
|
@@ -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
@@ -1,8 +1,9 @@
|
|
1
1
|
{
|
2
2
|
"name": "gpt-po",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.2.0",
|
4
4
|
"description": "command tool for translate po files by gpt",
|
5
5
|
"main": "lib/src/index.js",
|
6
|
+
"type": "module",
|
6
7
|
"bin": {
|
7
8
|
"gpt-po": "lib/src/index.js"
|
8
9
|
},
|
@@ -33,23 +34,23 @@
|
|
33
34
|
"openai"
|
34
35
|
],
|
35
36
|
"devDependencies": {
|
36
|
-
"@types/gettext-parser": "^4.0.
|
37
|
-
"@types/jest": "^29.5.
|
38
|
-
"jest": "^29.
|
37
|
+
"@types/gettext-parser": "^4.0.4",
|
38
|
+
"@types/jest": "^29.5.12",
|
39
|
+
"jest": "^29.7.0",
|
39
40
|
"ncp": "^2.0.0",
|
40
|
-
"prettier": "^3.
|
41
|
-
"rimraf": "^
|
42
|
-
"ts-jest": "^29.
|
41
|
+
"prettier": "^3.3.3",
|
42
|
+
"rimraf": "^6.0.1",
|
43
|
+
"ts-jest": "^29.2.5",
|
43
44
|
"tslint": "^6.1.3",
|
44
45
|
"tslint-config-prettier": "^1.18.0",
|
45
|
-
"typescript": "^5.
|
46
|
+
"typescript": "^5.5.4"
|
46
47
|
},
|
47
48
|
"dependencies": {
|
48
|
-
"commander": "^
|
49
|
-
"gettext-parser": "^
|
50
|
-
"openai": "^
|
49
|
+
"commander": "^12.1.0",
|
50
|
+
"gettext-parser": "^8.0.0",
|
51
|
+
"openai": "^4.56.0"
|
51
52
|
},
|
52
53
|
"engines": {
|
53
|
-
"node": ">=
|
54
|
+
"node": ">=18.0.0"
|
54
55
|
}
|
55
56
|
}
|
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,48 +1,30 @@
|
|
1
|
-
#!/usr/bin/env node
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
const commander_1 = require("commander");
|
14
|
-
const pkg = require("../package.json");
|
15
|
-
const sync_1 = require("./sync");
|
16
|
-
const translate_1 = require("./translate");
|
17
|
-
const utils_1 = require("./utils");
|
18
|
-
const manipulate_1 = require("./manipulate");
|
19
|
-
const program = new commander_1.Command();
|
1
|
+
#!/usr/bin/env node --no-warnings=ExperimentalWarning
|
2
|
+
import { Command, Option } from "commander";
|
3
|
+
import path from "path";
|
4
|
+
import { fileURLToPath } from "url";
|
5
|
+
import pkg from "../package.json" with { type: "json" };
|
6
|
+
import { sync } from "./sync.js";
|
7
|
+
import { init, translatePo, translatePoDir } from "./translate.js";
|
8
|
+
import { copyFileIfNotExists, compilePo, findConfig, openFileByDefault, openFileExplorer, parsePo } from "./utils.js";
|
9
|
+
import { removeByOptions } from "./manipulate.js";
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
11
|
+
const __dirname = path.dirname(__filename);
|
12
|
+
const program = new Command();
|
20
13
|
program.name(pkg.name).version(pkg.version).description(pkg.description);
|
21
14
|
program
|
22
15
|
.command("translate", { isDefault: true })
|
23
16
|
.description("translate po file (default command)")
|
24
|
-
.addOption(new
|
25
|
-
.addOption(new
|
26
|
-
.addOption(new
|
27
|
-
.
|
28
|
-
.
|
29
|
-
"gpt-4o",
|
30
|
-
"gpt-4-turbo",
|
31
|
-
"gpt-4",
|
32
|
-
"gpt-4-0314",
|
33
|
-
"gpt-4-32k",
|
34
|
-
"gpt-4-32k-0314",
|
35
|
-
"gpt-3.5-turbo",
|
36
|
-
"gpt-3.5-turbo-0301",
|
37
|
-
]))
|
38
|
-
.addOption(new commander_1.Option("--po <file>", "po file path").conflicts("dir"))
|
39
|
-
.addOption(new commander_1.Option("--dir <dir>", "po file directory").conflicts("po"))
|
17
|
+
.addOption(new Option("-k, --key <key>", "openai api key").env("OPENAI_API_KEY"))
|
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-mini"))
|
20
|
+
.addOption(new Option("--po <file>", "po file path").conflicts("dir"))
|
21
|
+
.addOption(new Option("--dir <dir>", "po file directory").conflicts("po"))
|
40
22
|
.option("-src, --source <lang>", "source language (ISO 639-1)", "en")
|
41
23
|
.option("-l, --lang <lang>", "target language (ISO 639-1)")
|
42
24
|
.option("--verbose", "print verbose log")
|
43
25
|
.option("--context <file>", "text file that provides the bot additional context")
|
44
|
-
.addOption(new
|
45
|
-
.action((args) =>
|
26
|
+
.addOption(new Option("-o, --output <file>", "output file path, overwirte po file by default").conflicts("dir"))
|
27
|
+
.action(async (args) => {
|
46
28
|
const { key, host, model, po, dir, source, lang, verbose, output, context } = args;
|
47
29
|
if (host) {
|
48
30
|
process.env.OPENAI_API_HOST = host;
|
@@ -55,60 +37,45 @@ program
|
|
55
37
|
console.error("OPENAI_API_KEY is required");
|
56
38
|
process.exit(1);
|
57
39
|
}
|
58
|
-
|
40
|
+
init();
|
59
41
|
if (po) {
|
60
|
-
|
42
|
+
await translatePo(model, po, source, lang, verbose, output, context);
|
61
43
|
}
|
62
44
|
else if (dir) {
|
63
|
-
|
45
|
+
await translatePoDir(model, dir, source, lang, verbose, context);
|
64
46
|
}
|
65
47
|
else {
|
66
48
|
console.error("po file or directory is required");
|
67
49
|
process.exit(1);
|
68
50
|
}
|
69
|
-
})
|
51
|
+
});
|
70
52
|
program
|
71
53
|
.command("sync")
|
72
54
|
.description("update po from pot file")
|
73
55
|
.requiredOption("--po <file>", "po file path")
|
74
56
|
.requiredOption("--pot <file>", "pot file path")
|
75
|
-
.action(({ po, pot }) =>
|
76
|
-
|
77
|
-
}));
|
78
|
-
// program command `systemprompt` with help text `open/edit system prompt`
|
79
|
-
program
|
80
|
-
.command("systemprompt")
|
81
|
-
.description("open/edit system prompt")
|
82
|
-
.option("--reset", "reset system prompt to default")
|
83
|
-
.action((args) => {
|
84
|
-
const { reset } = args;
|
85
|
-
// open `systemprompt.txt` file by system text default editor
|
86
|
-
const copyFile = __dirname + "/systemprompt.txt";
|
87
|
-
// user home path
|
88
|
-
const promptFile = (0, utils_1.findConfig)("systemprompt.txt");
|
89
|
-
(0, utils_1.copyFileIfNotExists)(promptFile, copyFile, reset);
|
90
|
-
if (reset) {
|
91
|
-
console.log("systemprompt.txt reset to default");
|
92
|
-
}
|
93
|
-
(0, utils_1.openFileByDefault)(promptFile);
|
57
|
+
.action(async ({ po, pot }) => {
|
58
|
+
await sync(po, pot);
|
94
59
|
});
|
95
60
|
// program command `userdict` with help text `open/edit user dictionary`
|
96
61
|
program
|
97
62
|
.command("userdict")
|
98
63
|
.description("open/edit user dictionary")
|
99
64
|
.option("--explore", "open user dictionary directory")
|
65
|
+
.option("-l, --lang <lang>", "target language (ISO 639-1)")
|
100
66
|
.action((args) => {
|
101
|
-
const { explore } = args;
|
67
|
+
const { explore, lang } = args;
|
102
68
|
// open `dictionary.json` file by system text default editor
|
103
69
|
const copyFile = __dirname + "/dictionary.json";
|
104
|
-
// user home path
|
105
|
-
const dictFile = (
|
70
|
+
// find from user home path
|
71
|
+
const dictFile = findConfig(`dictionary${lang ? "-" + lang : ""}.json`);
|
106
72
|
if (explore) {
|
107
73
|
// open user dictionary directory
|
108
|
-
return
|
74
|
+
return openFileExplorer(dictFile);
|
109
75
|
}
|
110
|
-
|
111
|
-
|
76
|
+
if (!lang)
|
77
|
+
copyFileIfNotExists(dictFile, copyFile);
|
78
|
+
openFileByDefault(dictFile);
|
112
79
|
});
|
113
80
|
// program command `remove` with help text `remove po entries by options`
|
114
81
|
program
|
@@ -122,7 +89,7 @@ program
|
|
122
89
|
.option("-tnf, --translated-not-fuzzy", "remove translated not fuzzy entries")
|
123
90
|
.option("-ft, --fuzzy-translated", "remove fuzzy translated entries")
|
124
91
|
.option("-rc, --reference-contains <text>", "remove entries whose reference contains text, text can be a regular expression like /text/ig")
|
125
|
-
.action((args) =>
|
92
|
+
.action(async (args) => {
|
126
93
|
const { po, fuzzy, obsolete, untranslated, translated, translatedNotFuzzy, fuzzyTranslated, referenceContains } = args;
|
127
94
|
const options = {
|
128
95
|
fuzzy,
|
@@ -131,12 +98,12 @@ program
|
|
131
98
|
translated,
|
132
99
|
translatedNotFuzzy,
|
133
100
|
fuzzyTranslated,
|
134
|
-
referenceContains
|
101
|
+
referenceContains
|
135
102
|
};
|
136
103
|
const output = args.output || po;
|
137
|
-
const translations =
|
138
|
-
|
104
|
+
const translations = await parsePo(po);
|
105
|
+
await compilePo(removeByOptions(translations, options), output);
|
139
106
|
console.log("done");
|
140
|
-
})
|
107
|
+
});
|
141
108
|
program.parse(process.argv);
|
142
109
|
//# sourceMappingURL=index.js.map
|
package/lib/src/manipulate.js
CHANGED
@@ -1,52 +1,48 @@
|
|
1
|
-
"use strict";
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.removeByOptions = void 0;
|
4
1
|
/**
|
5
2
|
* remove entity by options
|
6
3
|
*/
|
7
|
-
function removeByOptions(potrans, options) {
|
8
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
4
|
+
export function removeByOptions(potrans, options) {
|
9
5
|
const fuzzyRegx = /\bfuzzy\b/;
|
10
6
|
const obsoleteRegx = /\bobsolete\b/;
|
11
|
-
const refRegMatch =
|
12
|
-
? /^\/([^\/]+)\/([igmsuy]*)/.exec(options
|
7
|
+
const refRegMatch = options?.referenceContains
|
8
|
+
? /^\/([^\/]+)\/([igmsuy]*)/.exec(options?.referenceContains)
|
13
9
|
: null;
|
14
10
|
const refRegx = refRegMatch ? new RegExp(refRegMatch[1], refRegMatch[2]) : null;
|
15
11
|
for (const [ctx, entries] of Object.entries(potrans.translations)) {
|
16
12
|
for (const [msgid, entry] of Object.entries(entries)) {
|
17
13
|
const msgstr = entry.msgstr.join("\n");
|
18
14
|
// remove fuzzy
|
19
|
-
if (
|
15
|
+
if (options?.fuzzy && fuzzyRegx.test(entry.comments?.flag || "")) {
|
20
16
|
delete potrans.translations[ctx][msgid];
|
21
17
|
}
|
22
18
|
// remove obsolete
|
23
|
-
if (
|
19
|
+
if (options?.obsolete && obsoleteRegx.test(entry.comments?.flag || "")) {
|
24
20
|
delete potrans.translations[ctx][msgid];
|
25
21
|
}
|
26
22
|
// remove untranslated
|
27
|
-
if (
|
23
|
+
if (options?.untranslated && msgstr.length === 0) {
|
28
24
|
delete potrans.translations[ctx][msgid];
|
29
25
|
}
|
30
26
|
// remove translated
|
31
|
-
if (
|
27
|
+
if (options?.translated && msgstr.length > 0) {
|
32
28
|
delete potrans.translations[ctx][msgid];
|
33
29
|
}
|
34
30
|
// remove translated not fuzzy
|
35
|
-
if (
|
31
|
+
if (options?.translatedNotFuzzy && msgstr.length > 0 && !fuzzyRegx.test(entry.comments?.flag || "")) {
|
36
32
|
delete potrans.translations[ctx][msgid];
|
37
33
|
}
|
38
34
|
// remove fuzzy translated
|
39
|
-
if (
|
35
|
+
if (options?.fuzzyTranslated && msgstr.length > 0 && fuzzyRegx.test(entry.comments?.flag || "")) {
|
40
36
|
delete potrans.translations[ctx][msgid];
|
41
37
|
}
|
42
38
|
// remove reference contains
|
43
|
-
if (options
|
39
|
+
if (options?.referenceContains) {
|
44
40
|
if (refRegx) {
|
45
|
-
if (
|
41
|
+
if (entry.comments?.reference && refRegx.test(entry.comments?.reference)) {
|
46
42
|
delete potrans.translations[ctx][msgid];
|
47
43
|
}
|
48
44
|
}
|
49
|
-
else if (
|
45
|
+
else if (entry.comments?.reference?.includes(options?.referenceContains)) {
|
50
46
|
delete potrans.translations[ctx][msgid];
|
51
47
|
}
|
52
48
|
}
|
@@ -54,5 +50,4 @@ function removeByOptions(potrans, options) {
|
|
54
50
|
}
|
55
51
|
return potrans;
|
56
52
|
}
|
57
|
-
exports.removeByOptions = removeByOptions;
|
58
53
|
//# sourceMappingURL=manipulate.js.map
|
package/lib/src/sync.js
CHANGED
@@ -1,33 +1,18 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
exports.sync = void 0;
|
13
|
-
const utils_1 = require("./utils");
|
14
|
-
function sync(po, pot) {
|
15
|
-
return __awaiter(this, void 0, void 0, function* () {
|
16
|
-
const potrans = yield (0, utils_1.parsePo)(po);
|
17
|
-
const potrans2 = yield (0, utils_1.parsePo)(pot);
|
18
|
-
for (const [ctx, entries] of Object.entries(potrans2.translations)) {
|
19
|
-
// copy msgstr from potrans to potrans2
|
20
|
-
for (const [msgid, _] of Object.entries(entries)) {
|
21
|
-
if (potrans.translations[ctx] &&
|
22
|
-
potrans.translations[ctx][msgid] &&
|
23
|
-
potrans.translations[ctx][msgid].msgstr[0]) {
|
24
|
-
potrans2.translations[ctx][msgid] = potrans.translations[ctx][msgid];
|
25
|
-
}
|
1
|
+
import { compilePo, parsePo } from "./utils.js";
|
2
|
+
export async function sync(po, pot) {
|
3
|
+
const potrans = await parsePo(po);
|
4
|
+
const potrans2 = await parsePo(pot);
|
5
|
+
for (const [ctx, entries] of Object.entries(potrans2.translations)) {
|
6
|
+
// copy msgstr from potrans to potrans2
|
7
|
+
for (const [msgid, _] of Object.entries(entries)) {
|
8
|
+
if (potrans.translations[ctx] &&
|
9
|
+
potrans.translations[ctx][msgid] &&
|
10
|
+
potrans.translations[ctx][msgid].msgstr[0]) {
|
11
|
+
potrans2.translations[ctx][msgid] = potrans.translations[ctx][msgid];
|
26
12
|
}
|
27
13
|
}
|
28
|
-
|
29
|
-
|
30
|
-
|
14
|
+
}
|
15
|
+
potrans.translations = potrans2.translations;
|
16
|
+
await compilePo(potrans, po);
|
31
17
|
}
|
32
|
-
exports.sync = sync;
|
33
18
|
//# sourceMappingURL=sync.js.map
|
package/lib/src/systemprompt.txt
CHANGED
@@ -1,19 +1 @@
|
|
1
|
-
You are a language translation expert.
|
2
|
-
You will be translating text from one language to another.
|
3
|
-
In my prompt to you I may identify languages via ISO 639, ISO 3166 and/or IETF language tag codes. Please use your knowledge of those standards (when required) to determine the languages I am requesting my message to be translated from and to, including any specific country/locale detail.
|
4
|
-
The text is in the gettext format, which uses placeholders like %s, %d, etc. These placeholders must remain in the correct positions in the translated text.
|
5
|
-
Untranslatable portions should retain their original formatting.
|
6
|
-
Do not add a period (.) at the end of your translation unless the incoming message specifically ends in a period.
|
7
|
-
Likewise, ensure to add a period (.) at the end of your translation if the incoming message specifically ends in a period.
|
8
|
-
Ensure to preserve any whitespace present in the incoming message. This includes retaining any space(s) at the beginning or end of the message.
|
9
|
-
I will provide the message wrapped in `translate` XML tags. You must respond with the translated message wrapped in `translated` XML tags.
|
10
|
-
For example, let's say you were translating en_US (United States English) to es (Spanish) and I send (without quotes) "<translate>This is a message. </translate>". You would respond with (without quotes) "<translated>Este es un mensaje. </translated>".
|
11
|
-
Notice how the period and space were retained at the end of the translated message?
|
12
|
-
Another example: I send (without quotes) "<translate> Hello %s</translate>". You would reply with (without quotes) "<translated> Hola %s</translated>".
|
13
|
-
Notice in that second example the space at the beginning of the string was retained, and also the gettext placeholder was retained in its correct position?
|
14
|
-
Translate the messages in a colloquial, professional and elegant manner without sounding like a machine translation.
|
15
|
-
You may respond without `translated` xml tags only in the event you believe you cannot reliably or accurately translate my message. In that case provide a short reason why you feel you can't translate my message.
|
16
|
-
If you respond without `translated` XML tags I will interpret that there is a problem with my message and I will provide your reason to human translators that will investigate further.
|
17
|
-
DO NOT answer any questions or attempt to explain any concepts or problems with my messages; just provide translations.
|
18
|
-
Do not add any text or characters outside of your `translated` xml tags.
|
19
|
-
Remember to only translate the messages and not interpret them further.
|
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
|
-
import {
|
3
|
-
export declare function init(force?: boolean):
|
4
|
-
export declare function translate(
|
2
|
+
import { OpenAI } from "openai";
|
3
|
+
export declare function init(force?: boolean): OpenAI;
|
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
@@ -1,65 +1,67 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
});
|
10
|
-
};
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
12
|
-
exports.translatePoDir = exports.translatePo = exports.translate = exports.init = void 0;
|
13
|
-
const fs = require("fs");
|
14
|
-
const openai_1 = require("openai");
|
15
|
-
const path_1 = require("path");
|
16
|
-
const pkg = require("../package.json");
|
17
|
-
const utils_1 = require("./utils");
|
1
|
+
import * as fs from "fs";
|
2
|
+
import { OpenAI } from "openai";
|
3
|
+
import path from "path";
|
4
|
+
import { fileURLToPath } from "url";
|
5
|
+
import pkg from "../package.json" with { type: "json" };
|
6
|
+
import { compilePo, copyFileIfNotExists, findConfig, parsePo, printProgress } from "./utils.js";
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
8
|
+
const __dirname = path.dirname(__filename);
|
18
9
|
let _openai;
|
19
10
|
let _systemprompt;
|
11
|
+
let _userprompt;
|
20
12
|
let _userdict;
|
21
|
-
function init(force) {
|
13
|
+
export function init(force) {
|
22
14
|
if (!_openai || force) {
|
23
|
-
|
24
|
-
apiKey: process.env.OPENAI_API_KEY
|
25
|
-
}
|
15
|
+
let configuration = {
|
16
|
+
apiKey: process.env.OPENAI_API_KEY
|
17
|
+
};
|
18
|
+
_openai = new OpenAI(configuration);
|
26
19
|
if (process.env.OPENAI_API_HOST) {
|
27
|
-
|
20
|
+
_openai.baseURL = process.env.OPENAI_API_HOST.replace(/\/+$/, "") + "/v1";
|
28
21
|
}
|
29
|
-
_openai = new openai_1.OpenAIApi(configuration);
|
30
22
|
}
|
31
|
-
// load systemprompt.txt from
|
23
|
+
// load systemprompt.txt from project
|
32
24
|
if (!_systemprompt || force) {
|
33
|
-
|
34
|
-
|
35
|
-
|
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");
|
36
30
|
}
|
37
31
|
// load dictionary.json from homedir
|
38
32
|
if (!_userdict || force) {
|
39
|
-
const userdict =
|
40
|
-
|
41
|
-
_userdict = {
|
33
|
+
const userdict = findConfig("dictionary.json");
|
34
|
+
copyFileIfNotExists(userdict, path.join(__dirname, "dictionary.json"));
|
35
|
+
_userdict = { default: JSON.parse(fs.readFileSync(userdict, "utf-8")) };
|
42
36
|
}
|
43
37
|
return _openai;
|
44
38
|
}
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
.
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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({
|
64
|
+
model: model,
|
63
65
|
temperature: 0.1,
|
64
66
|
messages: [
|
65
67
|
{
|
@@ -68,95 +70,117 @@ function translate(text, src, lang, model, comments, contextFile) {
|
|
68
70
|
},
|
69
71
|
{
|
70
72
|
role: "user",
|
71
|
-
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
|
72
75
|
},
|
73
76
|
{
|
74
77
|
role: "assistant",
|
75
|
-
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.`
|
76
79
|
},
|
77
|
-
// add userdict
|
78
|
-
...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
|
79
88
|
{
|
80
89
|
role: "user",
|
81
|
-
content:
|
82
|
-
}
|
83
|
-
]
|
90
|
+
content: translationsContent
|
91
|
+
}
|
92
|
+
]
|
84
93
|
}, {
|
85
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
|
+
}
|
86
108
|
});
|
87
109
|
}
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
110
|
+
export async function translatePo(model, po, source, lang, verbose, output, contextFile) {
|
111
|
+
const potrans = await parsePo(po);
|
112
|
+
if (!lang)
|
113
|
+
lang = potrans.headers["Language"];
|
114
|
+
if (!lang) {
|
115
|
+
console.error("No language specified via po file or args");
|
116
|
+
return;
|
117
|
+
}
|
118
|
+
// try to load dictionary by lang-code if it not loaded
|
119
|
+
const lang_code = lang
|
120
|
+
.toLowerCase()
|
121
|
+
.trim()
|
122
|
+
.replace(/[\W_]+/g, "-");
|
123
|
+
if (!_userdict[lang_code]) {
|
124
|
+
const lang_dic_file = findConfig(`dictionary-${lang_code}.json`);
|
125
|
+
if (fs.existsSync(lang_dic_file)) {
|
126
|
+
_userdict[lang_code] = JSON.parse(fs.readFileSync(lang_dic_file, "utf-8"));
|
127
|
+
console.log(`dictionary-${lang_code}.json is loaded.`);
|
98
128
|
}
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
129
|
+
}
|
130
|
+
const list = [];
|
131
|
+
const trimRegx = /(?:^ )|(?: $)/;
|
132
|
+
let trimed = false;
|
133
|
+
for (const [ctx, entries] of Object.entries(potrans.translations)) {
|
134
|
+
for (const [msgid, trans] of Object.entries(entries)) {
|
135
|
+
if (msgid == "")
|
136
|
+
continue;
|
137
|
+
if (!trans.msgstr[0]) {
|
138
|
+
list.push(trans);
|
139
|
+
continue;
|
106
140
|
}
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
let trimed = false;
|
111
|
-
for (const [ctx, entries] of Object.entries(potrans.translations)) {
|
112
|
-
for (const [msgid, trans] of Object.entries(entries)) {
|
113
|
-
if (msgid == "")
|
114
|
-
continue;
|
115
|
-
if (!trans.msgstr[0]) {
|
116
|
-
list.push(trans);
|
117
|
-
continue;
|
118
|
-
}
|
119
|
-
else if (trimRegx.test(trans.msgstr[0])) {
|
120
|
-
trimed = true;
|
121
|
-
trans.msgstr[0] = trans.msgstr[0].trim();
|
122
|
-
}
|
141
|
+
else if (trimRegx.test(trans.msgstr[0])) {
|
142
|
+
trimed = true;
|
143
|
+
trans.msgstr[0] = trans.msgstr[0].trim();
|
123
144
|
}
|
124
145
|
}
|
125
|
-
|
126
|
-
|
146
|
+
}
|
147
|
+
if (trimed) {
|
148
|
+
await compilePo(potrans, po);
|
149
|
+
}
|
150
|
+
if (list.length == 0) {
|
151
|
+
console.log("done.");
|
152
|
+
return;
|
153
|
+
}
|
154
|
+
potrans.headers["Last-Translator"] = `gpt-po v${pkg.version}`;
|
155
|
+
const translations = [];
|
156
|
+
let err429 = false;
|
157
|
+
for (let i = 0, c = 0; i < list.length; i++) {
|
158
|
+
if (i == 0)
|
159
|
+
printProgress(i, list.length);
|
160
|
+
if (err429) {
|
161
|
+
// sleep for 20 seconds.
|
162
|
+
await new Promise((resolve) => setTimeout(resolve, 20000));
|
127
163
|
}
|
128
|
-
|
129
|
-
|
130
|
-
|
164
|
+
const trans = list[i];
|
165
|
+
if (c < 2000) {
|
166
|
+
translations.push(trans);
|
167
|
+
c += trans.msgid.length;
|
131
168
|
}
|
132
|
-
|
133
|
-
let err429 = false;
|
134
|
-
let modified = false;
|
135
|
-
for (let i = 0; i < list.length; i++) {
|
136
|
-
if (i == 0)
|
137
|
-
(0, utils_1.printProgress)(i, list.length);
|
138
|
-
if (err429) {
|
139
|
-
// sleep for 20 seconds.
|
140
|
-
yield new Promise((resolve) => setTimeout(resolve, 20000));
|
141
|
-
}
|
142
|
-
const trans = list[i];
|
169
|
+
if (c >= 2000 || i == list.length - 1) {
|
143
170
|
try {
|
144
|
-
|
145
|
-
var translated = ((_a = res.data.choices[0].message) === null || _a === void 0 ? void 0 : _a.content) || trans.msgstr[0];
|
146
|
-
if (!translated.startsWith('<translated>') && !translated.endsWith('</translated>')) {
|
147
|
-
// We got an error response
|
148
|
-
console.log("Error: Unable to translate string [" + trans.msgid + "]. Bot says [" + translated + "]");
|
149
|
-
continue;
|
150
|
-
}
|
151
|
-
// We got a valid translation response
|
152
|
-
trans.msgstr[0] = translated.replace('<translated>', '').replace('</translated>', '');
|
153
|
-
modified = true;
|
171
|
+
await translate(source, lang, model, translations, contextFile);
|
154
172
|
if (verbose) {
|
155
|
-
|
156
|
-
|
173
|
+
translations.forEach((trans) => {
|
174
|
+
console.log(trans.msgid);
|
175
|
+
console.log(trans.msgstr[0]);
|
176
|
+
});
|
157
177
|
}
|
158
|
-
|
159
|
-
|
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);
|
160
184
|
}
|
161
185
|
catch (error) {
|
162
186
|
if (error.response) {
|
@@ -178,21 +202,17 @@ function translatePo(model, po, source, lang, verbose, output, contextFile) {
|
|
178
202
|
}
|
179
203
|
}
|
180
204
|
}
|
181
|
-
|
182
|
-
|
205
|
+
}
|
206
|
+
console.log("done.");
|
183
207
|
}
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
console.log(`translating ${po}`);
|
192
|
-
yield translatePo(model, po, source, lang, verbose, po, contextFile);
|
193
|
-
}
|
208
|
+
export async function translatePoDir(model = "gpt-3.5-turbo", dir, source, lang, verbose, contextFile) {
|
209
|
+
const files = fs.readdirSync(dir);
|
210
|
+
for (const file of files) {
|
211
|
+
if (file.endsWith(".po")) {
|
212
|
+
const po = path.join(dir, file);
|
213
|
+
console.log(`translating ${po}`);
|
214
|
+
await translatePo(model, po, source, lang, verbose, po, contextFile);
|
194
215
|
}
|
195
|
-
}
|
216
|
+
}
|
196
217
|
}
|
197
|
-
exports.translatePoDir = translatePoDir;
|
198
218
|
//# sourceMappingURL=translate.js.map
|
@@ -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
@@ -1,18 +1,15 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
const gettext_parser_1 = require("gettext-parser");
|
7
|
-
const os_1 = require("os");
|
8
|
-
const path = require("path");
|
1
|
+
import { exec, spawn } from "child_process";
|
2
|
+
import * as fs from "fs";
|
3
|
+
import { po } from "gettext-parser";
|
4
|
+
import { homedir, platform } from "os";
|
5
|
+
import * as path from "path";
|
9
6
|
/**
|
10
7
|
* copy source file to destination file if destination file does not exist
|
11
8
|
* @param file destination file path
|
12
9
|
* @param copyFile source file path
|
13
10
|
* @param force force copy file
|
14
11
|
*/
|
15
|
-
function copyFileIfNotExists(file, copyFile, force = false) {
|
12
|
+
export function copyFileIfNotExists(file, copyFile, force = false) {
|
16
13
|
// make sure the directory exists
|
17
14
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
18
15
|
// check if file exists else create it
|
@@ -27,28 +24,25 @@ function copyFileIfNotExists(file, copyFile, force = false) {
|
|
27
24
|
fs.copyFileSync(copyFile, file);
|
28
25
|
}
|
29
26
|
}
|
30
|
-
|
31
|
-
function openFileByDefault(filePath) {
|
27
|
+
export function openFileByDefault(filePath) {
|
32
28
|
// Use the 'open' command on macOS or 'start' command on Windows to open the file with the default system editor
|
33
29
|
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
34
30
|
// Spawn a new process for the default editor and pass the file name as an argument
|
35
|
-
|
31
|
+
spawn(command, [filePath], { shell: true });
|
36
32
|
}
|
37
|
-
|
38
|
-
function parsePo(poFile, defaultCharset) {
|
33
|
+
export function parsePo(poFile, defaultCharset) {
|
39
34
|
// read poFile as buffer, then parse it
|
40
35
|
return new Promise((resolve, reject) => {
|
41
36
|
fs.readFile(poFile, (err, buffer) => {
|
42
37
|
if (err)
|
43
38
|
reject(err);
|
44
|
-
|
39
|
+
const result = po.parse(buffer, defaultCharset ?? "utf-8");
|
45
40
|
resolve(result);
|
46
41
|
});
|
47
42
|
});
|
48
43
|
}
|
49
|
-
|
50
|
-
|
51
|
-
const buffer = gettext_parser_1.po.compile(data, { foldLength: 120 });
|
44
|
+
export function compilePo(data, poFile, options = { foldLength: 120, sort: false, escapeCharacters: true }) {
|
45
|
+
const buffer = po.compile(data, options);
|
52
46
|
return new Promise((resolve, reject) => {
|
53
47
|
fs.writeFile(poFile, buffer, (err) => {
|
54
48
|
if (err)
|
@@ -57,8 +51,7 @@ function compilePo(data, poFile) {
|
|
57
51
|
});
|
58
52
|
});
|
59
53
|
}
|
60
|
-
|
61
|
-
function printProgress(progress, total, extra) {
|
54
|
+
export function printProgress(progress, total, extra) {
|
62
55
|
const percent = Math.floor((progress / total) * 100);
|
63
56
|
const bar = Array(Math.floor(percent / 5))
|
64
57
|
.fill("█")
|
@@ -68,8 +61,7 @@ function printProgress(progress, total, extra) {
|
|
68
61
|
.join("");
|
69
62
|
process.stdout.write(`\r${bar}${dots} ${percent}% ${progress}/${total} ${extra || ""}`);
|
70
63
|
}
|
71
|
-
|
72
|
-
function gitRootDir(dir) {
|
64
|
+
export function gitRootDir(dir) {
|
73
65
|
// if dir is not provided, use current working directory
|
74
66
|
dir = dir || process.cwd();
|
75
67
|
// check if dir is a git repository
|
@@ -87,7 +79,6 @@ function gitRootDir(dir) {
|
|
87
79
|
}
|
88
80
|
}
|
89
81
|
}
|
90
|
-
exports.gitRootDir = gitRootDir;
|
91
82
|
/**
|
92
83
|
* find config file in the following order:
|
93
84
|
* 1. current working directory of the Node.js process
|
@@ -96,15 +87,15 @@ exports.gitRootDir = gitRootDir;
|
|
96
87
|
* @param fileName
|
97
88
|
* @returns full path of the config file
|
98
89
|
*/
|
99
|
-
function findConfig(fileName) {
|
90
|
+
export function findConfig(fileName) {
|
100
91
|
const currentDir = process.cwd();
|
101
92
|
const gitDir = gitRootDir() || currentDir;
|
102
|
-
const homeDir =
|
93
|
+
const homeDir = homedir();
|
103
94
|
const filePaths = [
|
104
95
|
path.join(currentDir, ".gpt-po", fileName),
|
105
96
|
path.join(currentDir, fileName),
|
106
97
|
path.join(gitDir, ".gpt-po", fileName),
|
107
|
-
path.join(homeDir, ".gpt-po", fileName)
|
98
|
+
path.join(homeDir, ".gpt-po", fileName),
|
108
99
|
];
|
109
100
|
// check if file exists and return the first one
|
110
101
|
for (const filePath of filePaths) {
|
@@ -115,22 +106,20 @@ function findConfig(fileName) {
|
|
115
106
|
// if no file exists, return the default one
|
116
107
|
return path.join(homeDir, ".gpt-po", fileName);
|
117
108
|
}
|
118
|
-
exports.findConfig = findConfig;
|
119
109
|
/**
|
120
110
|
* open file explorer by platform
|
121
111
|
* @param location folder or file path
|
122
112
|
*/
|
123
|
-
function openFileExplorer(location) {
|
124
|
-
if (
|
125
|
-
|
113
|
+
export function openFileExplorer(location) {
|
114
|
+
if (platform() === "win32") {
|
115
|
+
exec(`explorer.exe "${path.dirname(location)}"`);
|
126
116
|
}
|
127
|
-
else if (
|
128
|
-
|
117
|
+
else if (platform() === "darwin") {
|
118
|
+
exec(`open "${path.dirname(location)}"`);
|
129
119
|
}
|
130
120
|
else {
|
131
121
|
// Assuming a Linux-based system
|
132
|
-
|
122
|
+
exec(`xdg-open "${path.dirname(location)}"`);
|
133
123
|
}
|
134
124
|
}
|
135
|
-
exports.openFileExplorer = openFileExplorer;
|
136
125
|
//# sourceMappingURL=utils.js.map
|
package/package.json
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
{
|
2
2
|
"name": "gpt-po",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.2.0",
|
4
4
|
"description": "command tool for translate po files by gpt",
|
5
5
|
"main": "lib/src/index.js",
|
6
|
+
"type": "module",
|
6
7
|
"bin": {
|
7
8
|
"gpt-po": "lib/src/index.js"
|
8
9
|
},
|
@@ -33,23 +34,23 @@
|
|
33
34
|
"openai"
|
34
35
|
],
|
35
36
|
"devDependencies": {
|
36
|
-
"@types/gettext-parser": "^4.0.
|
37
|
-
"@types/jest": "^29.5.
|
38
|
-
"jest": "^29.
|
37
|
+
"@types/gettext-parser": "^4.0.4",
|
38
|
+
"@types/jest": "^29.5.12",
|
39
|
+
"jest": "^29.7.0",
|
39
40
|
"ncp": "^2.0.0",
|
40
|
-
"prettier": "^3.
|
41
|
-
"rimraf": "^
|
42
|
-
"ts-jest": "^29.
|
41
|
+
"prettier": "^3.3.3",
|
42
|
+
"rimraf": "^6.0.1",
|
43
|
+
"ts-jest": "^29.2.5",
|
43
44
|
"tslint": "^6.1.3",
|
44
45
|
"tslint-config-prettier": "^1.18.0",
|
45
|
-
"typescript": "^5.
|
46
|
+
"typescript": "^5.5.4"
|
46
47
|
},
|
47
48
|
"dependencies": {
|
48
|
-
"commander": "^
|
49
|
-
"gettext-parser": "^
|
50
|
-
"openai": "^
|
49
|
+
"commander": "^12.1.0",
|
50
|
+
"gettext-parser": "^8.0.0",
|
51
|
+
"openai": "^4.56.0"
|
51
52
|
},
|
52
53
|
"engines": {
|
53
|
-
"node": ">=
|
54
|
+
"node": ">=18.0.0"
|
54
55
|
}
|
55
56
|
}
|
File without changes
|