gpt-po 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -54,8 +54,7 @@ translate po file (default command)
54
54
  Options:
55
55
  -k, --key <key> openai api key (env: OPENAI_API_KEY)
56
56
  --host <host> openai api host (env: OPENAI_API_HOST)
57
- --model <model> openai model (choices: "gpt-4o", "gpt-4-turbo", "gpt-4", "gpt-4-0314", "gpt-4-32k", "gpt-4-32k-0314", "gpt-3.5-turbo", "gpt-3.5-turbo-0301",
58
- default: "gpt-4o")
57
+ --model <model> openai model (default: "gpt-4o-mini", env: OPENAI_MODEL)
59
58
  --po <file> po file path
60
59
  --dir <dir> po file directory
61
60
  -src, --source <lang> source language (default: "english")
package/README_zh-CN.md CHANGED
@@ -56,7 +56,7 @@ npm install gpt-po
56
56
  选项:
57
57
  -k, --key <key> openai api key (环境变量: OPENAI_API_KEY)
58
58
  --host <host> openai api host (环境变量: OPENAI_API_HOST)
59
- --model <model> openai 模型 (选项: "gpt-4o", "gpt-4-turbo", "gpt-4", "gpt-4-0314", "gpt-4-32k", "gpt-4-32k-0314", "gpt-3.5-turbo", "gpt-3.5-turbo-0301", 默认: "gpt-4o")
59
+ --model <model> openai 模型 (默认: "gpt-4o-mini", 环境变量: OPENAI_MODEL)
60
60
  --po <file> po 文件路径
61
61
  --dir <dir> po 文件目录
62
62
  -src, --source <lang> 源语言 (默认: "english")
package/lib/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "gpt-po",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
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.2",
37
- "@types/jest": "^29.5.1",
38
- "jest": "^29.5.0",
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.0.3",
41
- "rimraf": "^5.0.0",
42
- "ts-jest": "^29.1.0",
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.0.4"
46
+ "typescript": "^5.5.4"
46
47
  },
47
48
  "dependencies": {
48
- "commander": "^10.0.1",
49
- "gettext-parser": "^6.0.0",
50
- "openai": "^3.2.1"
49
+ "commander": "^12.1.0",
50
+ "gettext-parser": "^8.0.0",
51
+ "openai": "^4.56.0"
51
52
  },
52
53
  "engines": {
53
- "node": ">=12.0.0"
54
+ "node": ">=18.0.0"
54
55
  }
55
56
  }
package/lib/src/index.js CHANGED
@@ -1,48 +1,30 @@
1
1
  #!/usr/bin/env node
2
- "use strict";
3
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
- return new (P || (P = Promise))(function (resolve, reject) {
6
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
- step((generator = generator.apply(thisArg, _arguments || [])).next());
10
- });
11
- };
12
- Object.defineProperty(exports, "__esModule", { value: true });
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();
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 commander_1.Option("-k, --key <key>", "openai api key").env("OPENAI_API_KEY"))
25
- .addOption(new commander_1.Option("--host <host>", "openai api host").env("OPENAI_API_HOST"))
26
- .addOption(new commander_1.Option("--model <model>", "openai model")
27
- .default("gpt-4o")
28
- .choices([
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"))
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 commander_1.Option("-o, --output <file>", "output file path, overwirte po file by default").conflicts("dir"))
45
- .action((args) => __awaiter(void 0, void 0, void 0, function* () {
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,26 +37,26 @@ program
55
37
  console.error("OPENAI_API_KEY is required");
56
38
  process.exit(1);
57
39
  }
58
- (0, translate_1.init)();
40
+ init();
59
41
  if (po) {
60
- yield (0, translate_1.translatePo)(model, po, source, lang, verbose, output, context);
42
+ await translatePo(model, po, source, lang, verbose, output, context);
61
43
  }
62
44
  else if (dir) {
63
- yield (0, translate_1.translatePoDir)(model, dir, source, lang, verbose, context);
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 }) => __awaiter(void 0, void 0, void 0, function* () {
76
- yield (0, sync_1.sync)(po, pot);
77
- }));
57
+ .action(async ({ po, pot }) => {
58
+ await sync(po, pot);
59
+ });
78
60
  // program command `systemprompt` with help text `open/edit system prompt`
79
61
  program
80
62
  .command("systemprompt")
@@ -85,12 +67,12 @@ program
85
67
  // open `systemprompt.txt` file by system text default editor
86
68
  const copyFile = __dirname + "/systemprompt.txt";
87
69
  // user home path
88
- const promptFile = (0, utils_1.findConfig)("systemprompt.txt");
89
- (0, utils_1.copyFileIfNotExists)(promptFile, copyFile, reset);
70
+ const promptFile = findConfig("systemprompt.txt");
71
+ copyFileIfNotExists(promptFile, copyFile, reset);
90
72
  if (reset) {
91
73
  console.log("systemprompt.txt reset to default");
92
74
  }
93
- (0, utils_1.openFileByDefault)(promptFile);
75
+ openFileByDefault(promptFile);
94
76
  });
95
77
  // program command `userdict` with help text `open/edit user dictionary`
96
78
  program
@@ -102,13 +84,13 @@ program
102
84
  // open `dictionary.json` file by system text default editor
103
85
  const copyFile = __dirname + "/dictionary.json";
104
86
  // user home path
105
- const dictFile = (0, utils_1.findConfig)("dictionary.json");
87
+ const dictFile = findConfig("dictionary.json");
106
88
  if (explore) {
107
89
  // open user dictionary directory
108
- return (0, utils_1.openFileExplorer)(dictFile);
90
+ return openFileExplorer(dictFile);
109
91
  }
110
- (0, utils_1.copyFileIfNotExists)(dictFile, copyFile);
111
- (0, utils_1.openFileByDefault)(dictFile);
92
+ copyFileIfNotExists(dictFile, copyFile);
93
+ openFileByDefault(dictFile);
112
94
  });
113
95
  // program command `remove` with help text `remove po entries by options`
114
96
  program
@@ -122,7 +104,7 @@ program
122
104
  .option("-tnf, --translated-not-fuzzy", "remove translated not fuzzy entries")
123
105
  .option("-ft, --fuzzy-translated", "remove fuzzy translated entries")
124
106
  .option("-rc, --reference-contains <text>", "remove entries whose reference contains text, text can be a regular expression like /text/ig")
125
- .action((args) => __awaiter(void 0, void 0, void 0, function* () {
107
+ .action(async (args) => {
126
108
  const { po, fuzzy, obsolete, untranslated, translated, translatedNotFuzzy, fuzzyTranslated, referenceContains } = args;
127
109
  const options = {
128
110
  fuzzy,
@@ -134,9 +116,9 @@ program
134
116
  referenceContains,
135
117
  };
136
118
  const output = args.output || po;
137
- const translations = yield (0, utils_1.parsePo)(po);
138
- yield (0, utils_1.compilePo)((0, manipulate_1.removeByOptions)(translations, options), output);
119
+ const translations = await parsePo(po);
120
+ await compilePo(removeByOptions(translations, options), output);
139
121
  console.log("done");
140
- }));
122
+ });
141
123
  program.parse(process.argv);
142
124
  //# sourceMappingURL=index.js.map
@@ -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 = (options === null || options === void 0 ? void 0 : options.referenceContains)
12
- ? /^\/([^\/]+)\/([igmsuy]*)/.exec(options === null || options === void 0 ? void 0 : options.referenceContains)
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 ((options === null || options === void 0 ? void 0 : options.fuzzy) && fuzzyRegx.test(((_a = entry.comments) === null || _a === void 0 ? void 0 : _a.flag) || "")) {
15
+ if (options?.fuzzy && fuzzyRegx.test(entry.comments?.flag || "")) {
20
16
  delete potrans.translations[ctx][msgid];
21
17
  }
22
18
  // remove obsolete
23
- if ((options === null || options === void 0 ? void 0 : options.obsolete) && obsoleteRegx.test(((_b = entry.comments) === null || _b === void 0 ? void 0 : _b.flag) || "")) {
19
+ if (options?.obsolete && obsoleteRegx.test(entry.comments?.flag || "")) {
24
20
  delete potrans.translations[ctx][msgid];
25
21
  }
26
22
  // remove untranslated
27
- if ((options === null || options === void 0 ? void 0 : options.untranslated) && msgstr.length === 0) {
23
+ if (options?.untranslated && msgstr.length === 0) {
28
24
  delete potrans.translations[ctx][msgid];
29
25
  }
30
26
  // remove translated
31
- if ((options === null || options === void 0 ? void 0 : options.translated) && msgstr.length > 0) {
27
+ if (options?.translated && msgstr.length > 0) {
32
28
  delete potrans.translations[ctx][msgid];
33
29
  }
34
30
  // remove translated not fuzzy
35
- if ((options === null || options === void 0 ? void 0 : options.translatedNotFuzzy) && msgstr.length > 0 && !fuzzyRegx.test(((_c = entry.comments) === null || _c === void 0 ? void 0 : _c.flag) || "")) {
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 ((options === null || options === void 0 ? void 0 : options.fuzzyTranslated) && msgstr.length > 0 && fuzzyRegx.test(((_d = entry.comments) === null || _d === void 0 ? void 0 : _d.flag) || "")) {
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 === null || options === void 0 ? void 0 : options.referenceContains) {
39
+ if (options?.referenceContains) {
44
40
  if (refRegx) {
45
- if (((_e = entry.comments) === null || _e === void 0 ? void 0 : _e.reference) && refRegx.test((_f = entry.comments) === null || _f === void 0 ? void 0 : _f.reference)) {
41
+ if (entry.comments?.reference && refRegx.test(entry.comments?.reference)) {
46
42
  delete potrans.translations[ctx][msgid];
47
43
  }
48
44
  }
49
- else if ((_h = (_g = entry.comments) === null || _g === void 0 ? void 0 : _g.reference) === null || _h === void 0 ? void 0 : _h.includes(options === null || options === void 0 ? void 0 : options.referenceContains)) {
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
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
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
- potrans.translations = potrans2.translations;
29
- yield (0, utils_1.compilePo)(potrans, po);
30
- });
14
+ }
15
+ potrans.translations = potrans2.translations;
16
+ await compilePo(potrans, po);
31
17
  }
32
- exports.sync = sync;
33
18
  //# sourceMappingURL=sync.js.map
@@ -1,19 +1,31 @@
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 translate text from one language to another, following these guidelines:
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,6 +1,6 @@
1
1
  import { GetTextTranslation } from "gettext-parser";
2
- import { OpenAIApi } from "openai";
3
- export declare function init(force?: boolean): OpenAIApi;
4
- export declare function translate(text: string, src: string, lang: string, model: string, comments: GetTextTranslation["comments"] | undefined, contextFile: string): Promise<import("axios").AxiosResponse<import("openai").CreateChatCompletionResponse, any>>;
2
+ import { OpenAI } from "openai";
3
+ export declare function init(force?: boolean): OpenAI;
4
+ export declare function translate(text: string, src: string, lang: string, model: string, comments: GetTextTranslation["comments"] | undefined, contextFile: string): import("openai/core.mjs").APIPromise<OpenAI.Chat.Completions.ChatCompletion>;
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>;
@@ -1,49 +1,39 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
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;
20
11
  let _userdict;
21
- function init(force) {
12
+ export function init(force) {
22
13
  if (!_openai || force) {
23
- const configuration = new openai_1.Configuration({
14
+ let configuration = {
24
15
  apiKey: process.env.OPENAI_API_KEY,
25
- });
16
+ };
17
+ _openai = new OpenAI(configuration);
26
18
  if (process.env.OPENAI_API_HOST) {
27
- configuration.basePath = process.env.OPENAI_API_HOST.replace(/\/+$/, "") + "/v1";
19
+ _openai.baseURL = process.env.OPENAI_API_HOST.replace(/\/+$/, "") + "/v1";
28
20
  }
29
- _openai = new openai_1.OpenAIApi(configuration);
30
21
  }
31
22
  // load systemprompt.txt from homedir
32
23
  if (!_systemprompt || force) {
33
- const systemprompt = (0, utils_1.findConfig)("systemprompt.txt");
34
- (0, utils_1.copyFileIfNotExists)(systemprompt, (0, path_1.join)(__dirname, "systemprompt.txt"));
24
+ const systemprompt = findConfig("systemprompt.txt");
25
+ copyFileIfNotExists(systemprompt, path.join(__dirname, "systemprompt.txt"));
35
26
  _systemprompt = fs.readFileSync(systemprompt, "utf-8");
36
27
  }
37
28
  // load dictionary.json from homedir
38
29
  if (!_userdict || force) {
39
- const userdict = (0, utils_1.findConfig)("dictionary.json");
40
- (0, utils_1.copyFileIfNotExists)(userdict, (0, path_1.join)(__dirname, "dictionary-template.json"));
30
+ const userdict = findConfig("dictionary.json");
31
+ copyFileIfNotExists(userdict, path.join(__dirname, "dictionary.json"));
41
32
  _userdict = { "default": JSON.parse(fs.readFileSync(userdict, "utf-8")) };
42
33
  }
43
34
  return _openai;
44
35
  }
45
- exports.init = init;
46
- function translate(text, src, lang, model, comments, contextFile) {
36
+ export function translate(text, src, lang, model, comments, contextFile) {
47
37
  const lang_code = lang.toLowerCase().trim().replace(/[\W_]+/g, "-");
48
38
  const dicts = Object.entries(_userdict[lang_code] || _userdict["default"])
49
39
  .filter(([k, _]) => text.toLowerCase().includes(k.toLowerCase()))
@@ -58,8 +48,8 @@ function translate(text, src, lang, model, comments, contextFile) {
58
48
  var context = "";
59
49
  if (contextFile !== undefined)
60
50
  context = "\n\n" + fs.readFileSync(contextFile, "utf-8");
61
- return _openai.createChatCompletion({
62
- model,
51
+ return _openai.chat.completions.create({
52
+ model: model,
63
53
  temperature: 0.1,
64
54
  messages: [
65
55
  {
@@ -85,114 +75,106 @@ function translate(text, src, lang, model, comments, contextFile) {
85
75
  timeout: 20000,
86
76
  });
87
77
  }
88
- exports.translate = translate;
89
- function translatePo(model, po, source, lang, verbose, output, contextFile) {
90
- var _a;
91
- return __awaiter(this, void 0, void 0, function* () {
92
- const potrans = yield (0, utils_1.parsePo)(po);
93
- if (!lang)
94
- lang = potrans.headers["Language"];
95
- if (!lang) {
96
- console.error("No language specified via po file or args");
97
- return;
78
+ export async function translatePo(model, po, source, lang, verbose, output, contextFile) {
79
+ const potrans = await parsePo(po);
80
+ if (!lang)
81
+ lang = potrans.headers["Language"];
82
+ if (!lang) {
83
+ console.error("No language specified via po file or args");
84
+ return;
85
+ }
86
+ // try to load dictionary by lang-code if it not loaded
87
+ const lang_code = lang.toLowerCase().trim().replace(/[\W_]+/g, "-");
88
+ if (!_userdict[lang_code]) {
89
+ const lang_dic_file = findConfig(`dictionary-${lang_code}.json`);
90
+ if (fs.existsSync(lang_dic_file)) {
91
+ _userdict[lang_code] = JSON.parse(fs.readFileSync(lang_dic_file, "utf-8"));
92
+ console.log(`dictionary-${lang_code}.json is loaded.`);
98
93
  }
99
- // try to load dictionary by lang-code if it not loaded
100
- const lang_code = lang.toLowerCase().trim().replace(/[\W_]+/g, "-");
101
- if (!_userdict[lang_code]) {
102
- const lang_dic_file = (0, utils_1.findConfig)(`dictionary-${lang_code}.json`);
103
- if (fs.existsSync(lang_dic_file)) {
104
- _userdict[lang_code] = JSON.parse(fs.readFileSync(lang_dic_file, "utf-8"));
105
- console.log(`dictionary-${lang_code}.json is loaded.`);
94
+ }
95
+ const list = [];
96
+ const trimRegx = /(?:^ )|(?: $)/;
97
+ let trimed = false;
98
+ for (const [ctx, entries] of Object.entries(potrans.translations)) {
99
+ for (const [msgid, trans] of Object.entries(entries)) {
100
+ if (msgid == "")
101
+ continue;
102
+ if (!trans.msgstr[0]) {
103
+ list.push(trans);
104
+ continue;
106
105
  }
107
- }
108
- const list = [];
109
- const trimRegx = /(?:^ )|(?: $)/;
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
- }
106
+ else if (trimRegx.test(trans.msgstr[0])) {
107
+ trimed = true;
108
+ trans.msgstr[0] = trans.msgstr[0].trim();
123
109
  }
124
110
  }
125
- if (trimed) {
126
- yield (0, utils_1.compilePo)(potrans, po);
127
- }
128
- if (list.length == 0) {
129
- console.log("done.");
130
- return;
111
+ }
112
+ if (trimed) {
113
+ await compilePo(potrans, po);
114
+ }
115
+ if (list.length == 0) {
116
+ console.log("done.");
117
+ return;
118
+ }
119
+ potrans.headers["Last-Translator"] = `gpt-po v${pkg.version}`;
120
+ let err429 = false;
121
+ let modified = false;
122
+ for (let i = 0; i < list.length; i++) {
123
+ if (i == 0)
124
+ printProgress(i, list.length);
125
+ if (err429) {
126
+ // sleep for 20 seconds.
127
+ await new Promise((resolve) => setTimeout(resolve, 20000));
131
128
  }
132
- potrans.headers["Last-Translator"] = `gpt-po v${pkg.version}`;
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));
129
+ const trans = list[i];
130
+ try {
131
+ const res = await translate(trans.msgid, source, lang, model, trans.comments, contextFile);
132
+ var translated = res.choices[0].message?.content || trans.msgstr[0];
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;
141
137
  }
142
- const trans = list[i];
143
- try {
144
- const res = yield translate(trans.msgid, source, lang, model, trans.comments, contextFile);
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;
154
- if (verbose) {
155
- console.log(trans.msgid);
156
- console.log(trans.msgstr[0]);
157
- }
158
- (0, utils_1.printProgress)(i + 1, list.length);
159
- yield (0, utils_1.compilePo)(potrans, output || po);
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]);
160
144
  }
161
- catch (error) {
162
- if (error.response) {
163
- if (error.response.status == 429) {
164
- // caused by rate limit exceeded, should sleep for 20 seconds.
165
- err429 = true;
166
- --i;
167
- }
168
- else {
169
- console.error(error.response.status);
170
- console.log(error.response.data);
171
- }
145
+ printProgress(i + 1, list.length);
146
+ await compilePo(potrans, output || po);
147
+ }
148
+ catch (error) {
149
+ if (error.response) {
150
+ if (error.response.status == 429) {
151
+ // caused by rate limit exceeded, should sleep for 20 seconds.
152
+ err429 = true;
153
+ --i;
172
154
  }
173
155
  else {
174
- console.error(error.message);
175
- if (error.code == "ECONNABORTED") {
176
- console.log('you may need to set "HTTPS_PROXY" to reach openai api.');
177
- }
156
+ console.error(error.response.status);
157
+ console.log(error.response.data);
158
+ }
159
+ }
160
+ else {
161
+ console.error(error.message);
162
+ if (error.code == "ECONNABORTED") {
163
+ console.log('you may need to set "HTTPS_PROXY" to reach openai api.');
178
164
  }
179
165
  }
180
166
  }
181
- console.log("done.");
182
- });
167
+ }
168
+ console.log("done.");
183
169
  }
184
- exports.translatePo = translatePo;
185
- function translatePoDir(model = "gpt-3.5-turbo", dir, source, lang, verbose, contextFile) {
186
- return __awaiter(this, void 0, void 0, function* () {
187
- const files = fs.readdirSync(dir);
188
- for (const file of files) {
189
- if (file.endsWith(".po")) {
190
- const po = (0, path_1.join)(dir, file);
191
- console.log(`translating ${po}`);
192
- yield translatePo(model, po, source, lang, verbose, po, contextFile);
193
- }
170
+ export async function translatePoDir(model = "gpt-3.5-turbo", dir, source, lang, verbose, contextFile) {
171
+ const files = fs.readdirSync(dir);
172
+ for (const file of files) {
173
+ if (file.endsWith(".po")) {
174
+ const po = path.join(dir, file);
175
+ console.log(`translating ${po}`);
176
+ await translatePo(model, po, source, lang, verbose, po, contextFile);
194
177
  }
195
- });
178
+ }
196
179
  }
197
- exports.translatePoDir = translatePoDir;
198
180
  //# sourceMappingURL=translate.js.map
package/lib/src/utils.js CHANGED
@@ -1,18 +1,15 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.openFileExplorer = exports.findConfig = exports.gitRootDir = exports.printProgress = exports.compilePo = exports.parsePo = exports.openFileByDefault = exports.copyFileIfNotExists = void 0;
4
- const child_process_1 = require("child_process");
5
- const fs = require("fs");
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
- exports.copyFileIfNotExists = copyFileIfNotExists;
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
- (0, child_process_1.spawn)(command, [filePath], { shell: true });
31
+ spawn(command, [filePath], { shell: true });
36
32
  }
37
- exports.openFileByDefault = openFileByDefault;
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
- var result = gettext_parser_1.po.parse(buffer, defaultCharset !== null && defaultCharset !== void 0 ? defaultCharset : "utf-8");
39
+ var result = po.parse(buffer, defaultCharset ?? "utf-8");
45
40
  resolve(result);
46
41
  });
47
42
  });
48
43
  }
49
- exports.parsePo = parsePo;
50
- function compilePo(data, poFile) {
51
- const buffer = gettext_parser_1.po.compile(data, { foldLength: 120 });
44
+ export function compilePo(data, poFile) {
45
+ const buffer = po.compile(data, { foldLength: 120 });
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
- exports.compilePo = compilePo;
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
- exports.printProgress = printProgress;
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,10 +87,10 @@ 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 = (0, os_1.homedir)();
93
+ const homeDir = homedir();
103
94
  const filePaths = [
104
95
  path.join(currentDir, ".gpt-po", fileName),
105
96
  path.join(currentDir, fileName),
@@ -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 ((0, os_1.platform)() === 'win32') {
125
- (0, child_process_1.exec)(`explorer.exe "${path.dirname(location)}"`);
113
+ export function openFileExplorer(location) {
114
+ if (platform() === 'win32') {
115
+ exec(`explorer.exe "${path.dirname(location)}"`);
126
116
  }
127
- else if ((0, os_1.platform)() === 'darwin') {
128
- (0, child_process_1.exec)(`open "${path.dirname(location)}"`);
117
+ else if (platform() === 'darwin') {
118
+ exec(`open "${path.dirname(location)}"`);
129
119
  }
130
120
  else {
131
121
  // Assuming a Linux-based system
132
- (0, child_process_1.exec)(`xdg-open "${path.dirname(location)}"`);
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.1.1",
3
+ "version": "1.1.2",
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.2",
37
- "@types/jest": "^29.5.1",
38
- "jest": "^29.5.0",
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.0.3",
41
- "rimraf": "^5.0.0",
42
- "ts-jest": "^29.1.0",
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.0.4"
46
+ "typescript": "^5.5.4"
46
47
  },
47
48
  "dependencies": {
48
- "commander": "^10.0.1",
49
- "gettext-parser": "^6.0.0",
50
- "openai": "^3.2.1"
49
+ "commander": "^12.1.0",
50
+ "gettext-parser": "^8.0.0",
51
+ "openai": "^4.56.0"
51
52
  },
52
53
  "engines": {
53
- "node": ">=12.0.0"
54
+ "node": ">=18.0.0"
54
55
  }
55
56
  }