cc-translate 0.6.0 → 1.0.1

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
@@ -1,52 +1,106 @@
1
1
  # cc-translate
2
- 一个基于Chat GPT的翻译库 🚀
3
2
 
4
- ## 特征
5
- - 支持通过一种源语言翻译出多国语言
6
- - 默认使用gpt-4o模型进行翻译,你也可以设置.gpt-4o性价比更高
3
+ An AI-powered i18n translation tool for JSON language files.
7
4
 
5
+ ## Features
8
6
 
9
- ## 安装
7
+ - Translate from one source language to multiple target languages.
8
+ - Cache-based incremental translation (only changed keys are translated).
9
+ - Concurrency queue with retry support for unstable API calls.
10
+ - Real-time progress bars and final success/failure summary.
11
+ - Cleaner logs for skipped files (`No content to translate` is aggregated).
12
+
13
+ ## Installation
10
14
 
11
15
  ```bash
16
+ npm install cc-translate
17
+ ```
12
18
 
13
- # 使用 npm
14
- npm install cc-translate -D
19
+ ## Quick Start
15
20
 
16
- # 或者使用 yarn
17
- yarn add cc-translate -D
21
+ ```ts
22
+ import path from "path";
23
+ import { I18nTranslateAgent } from "cc-translate";
18
24
 
19
- ```
25
+ const root = process.cwd();
20
26
 
21
- ## 快速开始
22
-
23
- ```mjs
24
- // translate.mjs
25
- import {CwalletTranslate} from 'cc-translate';
26
-
27
- // 获取当前文件的完整路径
28
- const __dirname = import.meta.dirname;
29
-
30
- // 创建一个translate实例
31
- const client = new CwalletTranslate({
32
- key: "your api key",
33
- sourceLanguage: 'en',
34
- // 缓存文件路径
35
- cacheFileRootPath: path.resolve(__dirname, './cache'),
36
- // 翻译源文件路径
37
- fileRootPath: path.resolve(__dirname, './langs'),
38
- // 调整翻译语境,告诉gpt我们的翻译要求。提高翻译准确性!
39
- fineTune: ['我们是一个区块链钱包', '请使用行业专业术语并更切合生活的翻译',....],
40
- // 需要翻译的语言列表
41
- languages: [
42
- 'zh-CN',
43
- 'zh-TW',
44
- 'ja',
45
- 'ar',
46
- ...
27
+ const agent = new I18nTranslateAgent({
28
+ // Required: OpenAI client options
29
+ openaiClientConfig: {
30
+ apiKey: process.env.OPENAI_API_KEY!,
31
+ // baseURL: "https://api.openai.com/v1",
32
+ },
33
+
34
+ // Required
35
+ cacheFileRootPath: path.resolve(root, "./cache"),
36
+ fileRootPath: path.resolve(root, "./locales"),
37
+ languages: ["zh-CN", "ja", "ko"],
38
+
39
+ // Optional
40
+ sourceLanguage: "en",
41
+ outputRootPath: path.resolve(root, "./locales"),
42
+ concurrency: 8,
43
+ fineTune: [
44
+ "This is a crypto wallet product",
45
+ "Keep blockchain terms consistent",
47
46
  ],
48
- // 输出文件路径
49
- outputRootPath: path.resolve(__dirname, './langs'),
47
+ chatCompletionCreateParams: {
48
+ model: "gpt-4o",
49
+ temperature: 0.2,
50
+ },
50
51
  });
51
52
 
52
- ```
53
+ await agent.translate();
54
+ ```
55
+
56
+ ## Directory Layout
57
+
58
+ ```text
59
+ your-project/
60
+ ├── locales/
61
+ │ └── en/
62
+ │ ├── common.json
63
+ │ └── auth.json
64
+ ├── cache/
65
+ │ ├── zh-CN/
66
+ │ └── ja/
67
+ └── scripts/translate.ts
68
+ ```
69
+
70
+ ## API
71
+
72
+ ### `I18nTranslateAgent`
73
+
74
+ | Option | Type | Required | Description |
75
+ | --- | --- | --- | --- |
76
+ | `openaiClientConfig` | `ClientOptions` | Yes | OpenAI SDK client options (e.g. `apiKey`, `baseURL`) |
77
+ | `cacheFileRootPath` | `string` | Yes | Cache root directory |
78
+ | `fileRootPath` | `string` | Yes | Source locale root directory |
79
+ | `languages` | `SupportLanguageType[]` | Yes | Target language codes |
80
+ | `sourceLanguage` | `SupportLanguageType` | No | Source language, default `en` |
81
+ | `outputRootPath` | `string` | No | Output directory, defaults to `fileRootPath` |
82
+ | `concurrency` | `number` | No | Queue length, default `8` |
83
+ | `fineTune` | `string[]` | No | Extra translation context lines |
84
+ | `chatCompletionCreateParams` | `Partial<ChatCompletionCreateParams>` | No | Extra chat completion params |
85
+
86
+ ### Utility Exports
87
+
88
+ ```ts
89
+ import {
90
+ generateCache,
91
+ deleteBatchCache,
92
+ } from "cc-translate";
93
+ ```
94
+
95
+ ## Log Behavior
96
+
97
+ During translation, skipped files with no diff/content are not printed one by one.
98
+ They are aggregated into a single summary line to avoid noisy logs in large projects.
99
+
100
+ ## Changelog
101
+
102
+ See `CHANGELOG.md`.
103
+
104
+ ## License
105
+
106
+ ISC
package/index.d.ts CHANGED
@@ -1,26 +1,37 @@
1
- import { OpenAI } from "openai";
2
- import { ICwalletTranslateParams, IJson, IOpenaiConfig, IOutputLanguageFile, ISingleTranslate, ITranslateChat, ITranslateChatResponse, SupportLanguageType } from "./types";
3
- export { testCompletions } from "./test/index.js";
1
+ import { ClientOptions, OpenAI } from "openai";
2
+ import { II18nTranslateAgentParams, IJson, IOutputLanguageFile, ISingleTranslate, ITranslateChat, ITranslateChatResponse, SupportLanguageType } from "./types";
3
+ import { ChatCompletionCreateParams } from "openai/resources";
4
4
  export { generateCache, deleteBatchCache } from "./lib/cache/index.js";
5
- export declare class CwalletTranslate {
6
- /** open ai api key */
7
- private OPENAI_KEY;
8
- /** */
5
+ export declare class I18nTranslateAgent {
6
+ /** 缓存文件根路径 */
9
7
  CACHE_ROOT_PATH: string;
8
+ /** 入口文件根路径 */
10
9
  ENTRY_ROOT_PATH: string;
10
+ /** 源语言 */
11
11
  /** default en */
12
12
  SOURCE_LANGUAGE: SupportLanguageType;
13
+ /** 输出文件根路径 */
13
14
  OUTPUT_ROOT_PATH: string | undefined;
15
+ /** 需要翻译的语言列表 */
14
16
  languages: SupportLanguageType[];
17
+ /** openai 客户端 */
15
18
  client: OpenAI | null;
16
- /** default model gpt-4o */
17
- openaiConfig: IOpenaiConfig;
19
+ /** 微调模型 */
18
20
  fineTune: string[];
19
- baseURL: string;
20
- constructor(params: ICwalletTranslateParams);
21
+ /** 翻译任务并发数(队列长度),完成一个即取下一个 */
22
+ concurrency: number;
23
+ /** openai 客户端配置 */
24
+ openaiClientConfig: ClientOptions;
25
+ /** openai 聊天完成创建参数 */
26
+ chatCompletionCreateParams: Partial<ChatCompletionCreateParams>;
27
+ constructor(params: II18nTranslateAgentParams);
28
+ /** 支持翻译的语言列表 */
21
29
  get supportLanguages(): import("./types").ILanguage[];
30
+ /** 输出文件根路径 */
22
31
  get outputPath(): string;
32
+ /** 搜索语言 */
23
33
  searchLanguage(code: SupportLanguageType): import("./types").ILanguage | undefined;
34
+ /** 创建openai客户端 */
24
35
  createOpenAIClient: () => void;
25
36
  /**
26
37
  * 翻译入口文件的所有支持的语言文件夹和其中的文件
package/index.js CHANGED
@@ -12,38 +12,37 @@ import fs from "fs";
12
12
  import colors from "ansi-colors";
13
13
  import { OpenAI } from "openai";
14
14
  import cliProgress from "cli-progress";
15
- import { chunkArray, getRandomNumber, notExistsToCreateFile, readFileOfDirSync, readJsonFileSync, } from "./lib/utils.js";
15
+ import { getRandomNumber, notExistsToCreateFile, readFileOfDirSync, readJsonFileSync, runWithConcurrencyLimit, } from "./lib/utils.js";
16
16
  import { getCacheFileSync, registerLanguageCacheFile, translateJSONDiffToJson, } from "./lib/cache/index.js";
17
17
  import { logErrorToFile } from "./lib/log/index.js";
18
18
  import { SUPPORT_LANGUAGE_MAP } from "./lib/support.js";
19
- export { testCompletions } from "./test/index.js";
20
19
  export { generateCache, deleteBatchCache } from "./lib/cache/index.js";
21
- const DEFAULT_OPENAI_CONFIG = {
20
+ const DEFAULT_CHAT_COMPLETION_CREATE_PARAMS = {
22
21
  model: "gpt-4o",
23
22
  };
24
- export class CwalletTranslate {
23
+ export class I18nTranslateAgent {
25
24
  constructor(params) {
26
- var _a, _b, _c, _d;
25
+ var _a, _b, _c, _d, _e, _f;
26
+ /** openai 客户端 */
27
27
  this.client = null;
28
+ /** 创建openai客户端 */
28
29
  this.createOpenAIClient = () => {
29
30
  /** 初始化openAi */
30
- const client = new OpenAI({
31
- apiKey: this.OPENAI_KEY,
32
- baseURL: this.baseURL,
33
- });
31
+ const client = new OpenAI(this.openaiClientConfig);
34
32
  this.client = client;
35
33
  };
36
34
  /**
37
35
  * 翻译入口文件的所有支持的语言文件夹和其中的文件
38
36
  */
39
37
  this.translate = () => __awaiter(this, void 0, void 0, function* () {
40
- console.log("🚀 开始翻译");
41
- console.log(`🚀 使用的模型: ${this.openaiConfig.model} 🚀`);
42
- console.log(`🚀 微调: ${this.fineTune} 🚀`);
38
+ console.log("🚀 Start translation");
39
+ console.log(`🚀 Using model: ${this.chatCompletionCreateParams.model} 🚀`);
40
+ console.log(`🚀 Fine-tuning: ${this.fineTune.join(",")} 🚀`);
41
+ console.log(`🚀 Concurrency queue length: ${this.concurrency} 🚀`);
43
42
  const translateFolderPath = path.join(this.ENTRY_ROOT_PATH, this.SOURCE_LANGUAGE);
44
43
  // 翻译源语言问价夹下的所有json文件
45
44
  const translateFolders = yield readFileOfDirSync(translateFolderPath);
46
- console.log("🚀 ~ 需要翻译语言的文件:", translateFolders);
45
+ console.log("🚀 ~ files to translate:", translateFolders, translateFolderPath);
47
46
  // 创建进度条
48
47
  const multiBar = new cliProgress.MultiBar({
49
48
  clearOnComplete: false,
@@ -51,8 +50,8 @@ export class CwalletTranslate {
51
50
  format: colors.cyan("{bar}") +
52
51
  "| {percentage}% || {filename} {value}/{total} ",
53
52
  }, cliProgress.Presets.legacy);
54
- let promises = [];
55
- const arr = [];
53
+ const taskList = [];
54
+ const skippedNoContent = [];
56
55
  for (const item of this.supportLanguages) {
57
56
  // 源语言不翻译
58
57
  if (item.code === this.SOURCE_LANGUAGE)
@@ -60,10 +59,10 @@ export class CwalletTranslate {
60
59
  for (const fileName of translateFolders) {
61
60
  const translateJson = yield this.getTranslateContent(item.code, fileName);
62
61
  if (!translateJson) {
63
- console.log(`${item.code}:${fileName} 没有需要翻译的内容`);
62
+ skippedNoContent.push(`${item.code}:${fileName}`);
64
63
  continue;
65
64
  }
66
- arr.push(() => this.singleTranslate({
65
+ taskList.push(() => this.singleTranslate({
67
66
  language: item.code,
68
67
  fileName,
69
68
  multiBar,
@@ -71,12 +70,21 @@ export class CwalletTranslate {
71
70
  }));
72
71
  }
73
72
  }
74
- promises = chunkArray(arr, 8);
75
- for (const chunk of promises) {
76
- yield Promise.all(chunk.map((fn) => fn()));
73
+ if (skippedNoContent.length > 0) {
74
+ const summary = skippedNoContent.length <= 5
75
+ ? skippedNoContent.join(", ")
76
+ : `${skippedNoContent.slice(0, 3).join(", ")} ... +${skippedNoContent.length - 3} more`;
77
+ console.log(`${colors.dim("Skipped (no content to translate):")} ${skippedNoContent.length} items${skippedNoContent.length <= 5 ? ` (${summary})` : ` — e.g. ${summary}`}`);
77
78
  }
79
+ // 并发队列:同时最多 concurrency 个任务,完成一个即从队列取下一个,避免长任务拖慢整批
80
+ const results = yield runWithConcurrencyLimit(taskList, this.concurrency);
78
81
  multiBar.stop();
79
- console.log("🚀 翻译完毕");
82
+ const errorLength = results.filter((item) => item.status === "rejected").length;
83
+ if (errorLength > 0) {
84
+ console.log(`${colors.red("Translation failed")}: ${errorLength} tasks`);
85
+ }
86
+ console.log(`${colors.green("Translation completed")}: ${results.length} tasks`);
87
+ console.log(`${colors.green("Translation successful")}: ${results.length - errorLength} tasks`);
80
88
  });
81
89
  /**
82
90
  * 翻译单个文件
@@ -142,34 +150,31 @@ export class CwalletTranslate {
142
150
  const targetLanguage = this.searchLanguage(language);
143
151
  const originLanguage = this.searchLanguage(this.SOURCE_LANGUAGE);
144
152
  if (!targetLanguage) {
145
- throw new Error(`不支持的语言:${language}`);
153
+ throw new Error(`Unsupported language:${language}`);
146
154
  }
147
155
  if (!originLanguage) {
148
- throw new Error(`不支持的语言:${this.SOURCE_LANGUAGE}`);
156
+ throw new Error(`Unsupported language:${this.SOURCE_LANGUAGE}`);
149
157
  }
150
158
  setTimeout(() => __awaiter(this, void 0, void 0, function* () {
151
159
  var _a, _b, _c;
152
- const chatCompletion = yield this.client.chat.completions.create({
153
- model: (_a = this.openaiConfig) === null || _a === void 0 ? void 0 : _a.model,
154
- messages: [
160
+ const chatCompletion = yield this.client.chat.completions.create(Object.assign(Object.assign({ model: (_a = this.chatCompletionCreateParams.model) !== null && _a !== void 0 ? _a : "gpt-4o" }, this.chatCompletionCreateParams), { stream: false, messages: [
155
161
  ...this.fineTune.map((val) => ({
156
162
  role: "system",
157
163
  content: val,
158
164
  })),
159
165
  {
160
166
  role: "system",
161
- content: `请将${originLanguage.name}翻译成${targetLanguage.name}`,
167
+ content: `Please translate ${originLanguage.name} to ${targetLanguage.name}`,
162
168
  },
163
169
  {
164
170
  role: "system",
165
- content: `翻译完成直接输出后对应意思的内容不要携带任何无关内容`,
171
+ content: `After translation is complete, directly output the corresponding meaning without any irrelevant content`,
166
172
  },
167
173
  {
168
174
  role: "user",
169
175
  content: value,
170
176
  },
171
- ],
172
- });
177
+ ] }));
173
178
  resolve({
174
179
  key,
175
180
  value: (_c = (_b = chatCompletion === null || chatCompletion === void 0 ? void 0 : chatCompletion.choices[0]) === null || _b === void 0 ? void 0 : _b.message.content) !== null && _c !== void 0 ? _c : value,
@@ -248,26 +253,40 @@ export class CwalletTranslate {
248
253
  folderName: this.CACHE_ROOT_PATH,
249
254
  });
250
255
  });
251
- this.OPENAI_KEY = params.key;
256
+ /** openai 客户端配置 */
257
+ this.openaiClientConfig = (_a = params.openaiClientConfig) !== null && _a !== void 0 ? _a : {};
258
+ /** openai 聊天完成创建参数 */
259
+ this.chatCompletionCreateParams =
260
+ (_b = params.chatCompletionCreateParams) !== null && _b !== void 0 ? _b : DEFAULT_CHAT_COMPLETION_CREATE_PARAMS;
261
+ /** 缓存文件根路径 */
252
262
  this.CACHE_ROOT_PATH = params.cacheFileRootPath;
263
+ /** 入口文件根路径 */
253
264
  this.ENTRY_ROOT_PATH = params.fileRootPath;
254
- this.openaiConfig = (_a = params.openaiConfig) !== null && _a !== void 0 ? _a : DEFAULT_OPENAI_CONFIG;
255
- this.SOURCE_LANGUAGE = (_b = params.sourceLanguage) !== null && _b !== void 0 ? _b : "en";
265
+ /** 源语言 */
266
+ this.SOURCE_LANGUAGE = (_c = params.sourceLanguage) !== null && _c !== void 0 ? _c : "en";
267
+ /** 输出文件根路径 */
256
268
  this.OUTPUT_ROOT_PATH = params.outputRootPath;
257
- this.fineTune = params.fineTune;
258
- this.languages = (_c = params.languages) !== null && _c !== void 0 ? _c : [];
259
- this.baseURL = (_d = params.baseURL) !== null && _d !== void 0 ? _d : "";
269
+ /** 微调模型 */
270
+ this.fineTune = (_d = params.fineTune) !== null && _d !== void 0 ? _d : [];
271
+ /** 需要翻译的语言列表 */
272
+ this.languages = (_e = params.languages) !== null && _e !== void 0 ? _e : [];
273
+ /** 翻译任务并发数(队列长度),完成一个即取下一个 */
274
+ this.concurrency = (_f = params.concurrency) !== null && _f !== void 0 ? _f : 8;
275
+ /** 创建openai客户端 */
260
276
  this.createOpenAIClient();
261
277
  }
278
+ /** 支持翻译的语言列表 */
262
279
  get supportLanguages() {
263
280
  return Object.entries(SUPPORT_LANGUAGE_MAP)
264
281
  .map(([key, val]) => val)
265
282
  .filter(({ code }) => this.languages.includes(code) || code === this.SOURCE_LANGUAGE);
266
283
  }
284
+ /** 输出文件根路径 */
267
285
  get outputPath() {
268
286
  var _a;
269
287
  return (_a = this.OUTPUT_ROOT_PATH) !== null && _a !== void 0 ? _a : this.ENTRY_ROOT_PATH;
270
288
  }
289
+ /** 搜索语言 */
271
290
  searchLanguage(code) {
272
291
  return this.supportLanguages.find((item) => item.code === code);
273
292
  }
package/lib/utils.d.ts CHANGED
@@ -23,4 +23,23 @@ export declare const isDirectoryPath: (path: string) => boolean;
23
23
  export declare const readFileOfDirSync: (dirPath: string) => string[];
24
24
  export declare function chunkArray<T extends object>(array: T[], chunkSize: number): T[][];
25
25
  export declare function intersection<T>(arr1: T[], arr2: T[]): T[];
26
+ export type TaskResult<T> = {
27
+ status: "fulfilled";
28
+ value: T;
29
+ } | {
30
+ status: "rejected";
31
+ reason: any;
32
+ };
33
+ /**
34
+ * 并发限制队列:同时最多运行 concurrency 个任务,任一任务完成后立即从队列取下一个执行,最大化利用并发槽位。
35
+ * @param tasks 返回 Promise 的函数数组(不立即执行,由队列按需执行)
36
+ * @param concurrency 并发数(队列长度)
37
+ */
38
+ export declare function runWithConcurrencyLimit<T>(
39
+ /** 返回 Promise 的函数数组(不立即执行,由队列按需执行) */
40
+ tasks: (() => Promise<T>)[],
41
+ /** 并发数(队列长度) */
42
+ concurrency: number,
43
+ /** 默认最大重试次数 */
44
+ maxRetries?: number): Promise<TaskResult<T>[]>;
26
45
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/lib/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAEjD;;;GAGG;AACH,eAAO,MAAM,qBAAqB,SAAU,MAAM,SAGjD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,SAAgB,MAAM,iBAUlD,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,WAAY,qBAAqB,SAS3D,CAAC;AAEF,eAAO,MAAM,eAAe,QAAS,MAAM,OAAO,MAAM,WAEvD,CAAC;AAEF,eAAO,MAAM,eAAe,SAAU,MAAM,YAG3C,CAAC;AAEF,eAAO,MAAM,iBAAiB,YAAa,MAAM,aAKhD,CAAC;AAEF,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,SASzE;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAczD"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/lib/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAEjD;;;GAGG;AACH,eAAO,MAAM,qBAAqB,SAAU,MAAM,SAGjD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,SAAgB,MAAM,iBAUlD,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,WAAY,qBAAqB,SAS3D,CAAC;AAEF,eAAO,MAAM,eAAe,QAAS,MAAM,OAAO,MAAM,WAEvD,CAAC;AAEF,eAAO,MAAM,eAAe,SAAU,MAAM,YAG3C,CAAC;AAEF,eAAO,MAAM,iBAAiB,YAAa,MAAM,aAKhD,CAAC;AAEF,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,SASzE;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAczD;AAGD,MAAM,MAAM,UAAU,CAAC,CAAC,IACpB;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GACjC;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,CAAC;AAExC;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,CAAC;AAC7C,sCAAsC;AACtC,KAAK,EAAE,CAAC,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE;AAC3B,gBAAgB;AAChB,WAAW,EAAE,MAAM;AACnB,eAAe;AACf,UAAU,GAAE,MAAU,GACrB,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAgD1B"}
package/lib/utils.js CHANGED
@@ -86,3 +86,61 @@ export function intersection(arr1, arr2) {
86
86
  }
87
87
  return [...intersectionSet];
88
88
  }
89
+ /**
90
+ * 并发限制队列:同时最多运行 concurrency 个任务,任一任务完成后立即从队列取下一个执行,最大化利用并发槽位。
91
+ * @param tasks 返回 Promise 的函数数组(不立即执行,由队列按需执行)
92
+ * @param concurrency 并发数(队列长度)
93
+ */
94
+ export function runWithConcurrencyLimit(tasks_1, concurrency_1) {
95
+ return __awaiter(this, arguments, void 0, function* (
96
+ /** 返回 Promise 的函数数组(不立即执行,由队列按需执行) */
97
+ tasks,
98
+ /** 并发数(队列长度) */
99
+ concurrency,
100
+ /** 默认最大重试次数 */
101
+ maxRetries = 3) {
102
+ if (tasks.length === 0)
103
+ return [];
104
+ // 新增:用于按顺序保存所有任务的结果
105
+ const results = new Array(tasks.length);
106
+ let index = 0;
107
+ function worker() {
108
+ return __awaiter(this, void 0, void 0, function* () {
109
+ while (true) {
110
+ const currentIndex = index++;
111
+ if (currentIndex >= tasks.length)
112
+ return;
113
+ let attempt = 0;
114
+ let isSuccess = false;
115
+ // 新增:容错与重试循环
116
+ while (attempt < maxRetries && !isSuccess) {
117
+ try {
118
+ const result = yield tasks[currentIndex]();
119
+ // 成功则记录结果,并跳出重试循环
120
+ results[currentIndex] = { status: "fulfilled", value: result };
121
+ isSuccess = true;
122
+ }
123
+ catch (error) {
124
+ attempt++;
125
+ console.warn(`[Task ${currentIndex}] 失败 (${attempt}/${maxRetries}):`, error);
126
+ if (attempt >= maxRetries) {
127
+ // 达到最大重试次数,依然失败,记录错误,让 worker 能够继续接手下一个任务
128
+ results[currentIndex] = { status: "rejected", reason: error };
129
+ }
130
+ else {
131
+ // 选做:在重试前稍微等一下(退避策略),避免被 API 连续限流拦截
132
+ // 每次重试等待时间递增:1秒, 2秒...
133
+ yield new Promise((resolve) => setTimeout(resolve, 1000 * attempt));
134
+ }
135
+ }
136
+ }
137
+ }
138
+ });
139
+ }
140
+ const workerCount = Math.min(concurrency, tasks.length);
141
+ // 等待所有 worker 把活干完(由于内部有 try-catch,这里绝对不会被 reject)
142
+ yield Promise.all(Array.from({ length: workerCount }, () => worker()));
143
+ // 统一返回完整的结果数组,且顺序与传入的 tasks 完美对应
144
+ return results;
145
+ });
146
+ }
package/package.json CHANGED
@@ -1,47 +1,65 @@
1
1
  {
2
2
  "name": "cc-translate",
3
- "version": "0.6.0",
3
+ "version": "1.0.1",
4
4
  "description": "A language translation tool based on OpenAI",
5
5
  "author": "maybe",
6
+ "license": "ISC",
7
+ "type": "module",
6
8
  "packageManager": "yarn@1.22.22",
9
+ "main": "./index.js",
10
+ "module": "./index.js",
7
11
  "types": "./index.d.ts",
8
- "main": "./index.mjs",
9
- "module": "./index.mjs",
10
- "publishConfig": {
11
- "access": "public"
12
+ "exports": {
13
+ ".": {
14
+ "types": "./index.d.ts",
15
+ "import": "./index.js",
16
+ "require": "./index.js",
17
+ "default": "./index.js"
18
+ }
12
19
  },
13
- "keywords": [
14
- "translate",
15
- "openai"
20
+ "files": [
21
+ "index.js",
22
+ "index.d.ts",
23
+ "lib",
24
+ "types"
16
25
  ],
17
- "license": "ISC",
26
+ "scripts": {
27
+ "compile": "tsc",
28
+ "prepublishOnly": "npm run compile",
29
+ "release": "npm run compile && npm publish"
30
+ },
18
31
  "repository": {
19
32
  "type": "git",
20
33
  "url": "git+https://github.com/cctip/cwallet-translate-script.git"
21
34
  },
22
- "homepage": "https://github.com/cctip/cwallet-translate-script",
23
- "bugs": "https://github.com/xxx/cwallet-translate-script/issues",
24
- "type": "module",
25
- "scripts": {
26
- "publish": "npx tsc && npm publish",
27
- "compile": "tsc"
35
+ "homepage": "https://github.com/cctip/cwallet-translate-script#readme",
36
+ "bugs": {
37
+ "url": "https://github.com/cctip/cwallet-translate-script/issues"
28
38
  },
29
- "exports": {
30
- ".": {
31
- "import": "./index.js",
32
- "require": "./index.js"
33
- }
39
+ "keywords": [
40
+ "translate",
41
+ "openai",
42
+ "i18n",
43
+ "translation",
44
+ "cli"
45
+ ],
46
+ "engines": {
47
+ "node": ">=18.0.0"
48
+ },
49
+ "sideEffects": false,
50
+ "publishConfig": {
51
+ "access": "public"
34
52
  },
35
53
  "dependencies": {
36
- "@google/genai": "^1.15.0",
54
+ "@google/genai": "^1.43.0",
37
55
  "ansi-colors": "^4.1.3",
38
56
  "cli-progress": "^3.12.0",
39
- "openai": "^4.68.1",
40
- "ts-node": "^10.9.2"
57
+ "openai": "^6.25.0"
41
58
  },
42
59
  "devDependencies": {
43
60
  "@types/cli-progress": "^3.11.6",
44
61
  "@types/node": "^22.7.9",
62
+ "ts-node": "^10.9.2",
45
63
  "typescript": "^5.6.3"
46
64
  }
47
65
  }
package/types/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { MultiBar } from "cli-progress";
2
- import OpenAI from "openai";
2
+ import { ClientOptions } from "openai";
3
+ import { ChatCompletionCreateParams } from "openai/resources";
3
4
  export interface ILanguage {
4
5
  /** 语言code */
5
6
  code: SupportLanguageType;
@@ -58,27 +59,29 @@ export interface ITranslateChatResponse {
58
59
  index: number;
59
60
  error?: Error;
60
61
  }
61
- export interface ICwalletTranslateParams {
62
- key: string;
62
+ export interface II18nTranslateAgentParams {
63
+ /** openai 客户端配置 */
64
+ openaiClientConfig: ClientOptions;
65
+ /** openai 聊天完成创建参数 */
66
+ chatCompletionCreateParams?: Partial<ChatCompletionCreateParams>;
67
+ /** 缓存文件根路径 */
63
68
  cacheFileRootPath: string;
64
- /** await translate file root path */
69
+ /** 入口文件根路径 */
65
70
  fileRootPath: string;
66
- fineTune: string[];
71
+ /** 源语言 */
72
+ /** default en */
73
+ sourceLanguage?: SupportLanguageType;
74
+ /** 需要翻译的语言列表 */
67
75
  languages: SupportLanguageType[];
76
+ /** 输出文件根路径 */
68
77
  outputRootPath?: string;
69
- sourceLanguage?: SupportLanguageType;
70
- openaiConfig?: IOpenaiConfig;
71
- baseURL?: string;
78
+ /** 翻译任务并发数(队列长度),完成一个即取下一个 */
79
+ concurrency?: number;
80
+ /** 微调模型 */
81
+ fineTune?: string[];
72
82
  }
73
83
  export interface ITranslate {
74
84
  }
75
- export interface IOpenaiConfig {
76
- model: OpenAI.Chat.ChatModel;
77
- }
78
- export interface ITestParams {
79
- key: string;
80
- question: string;
81
- }
82
85
  export interface IGenerateCacheParams {
83
86
  sourceFolderPath: string;
84
87
  sourceLanguage: SupportLanguageType;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/types/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/types/index.ts"],"names":[],"mappings":"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"}
package/index.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EACL,uBAAuB,EACvB,KAAK,EACL,aAAa,EACb,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,sBAAsB,EACtB,mBAAmB,EACpB,MAAM,SAAS,CAAC;AAgBjB,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAMvE,qBAAa,gBAAgB;IAC3B,uBAAuB;IACvB,OAAO,CAAC,UAAU,CAAS;IAC3B,MAAM;IACN,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB;IACjB,eAAe,EAAE,mBAAmB,CAAC;IACrC,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,SAAS,EAAE,mBAAmB,EAAE,CAAC;IACjC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC7B,2BAA2B;IAC3B,YAAY,EAAE,aAAa,CAAC;IAC5B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;gBAEJ,MAAM,EAAE,uBAAuB;IAa3C,IAAI,gBAAgB,kCAOnB;IAED,IAAI,UAAU,WAEb;IAED,cAAc,CAAC,IAAI,EAAE,mBAAmB;IAIxC,kBAAkB,aAQhB;IACF;;OAEG;IACH,SAAS,sBA0DP;IACF;;;;OAIG;IACH,eAAe,WAAkB,gBAAgB,mBAoD/C;IAEF;;;;;;;OAOG;IACH,aAAa,WAAY,cAAc,KAAG,OAAO,CAAC,sBAAsB,CAAC,CA2DvE;IACF;;;;;OAKG;IACH,mBAAmB,aACP,mBAAmB,YACnB,MAAM,KACf,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CA6B3B;IAEF;;;OAGG;IACH,kBAAkB,WAAkB,mBAAmB,mBAsCrD;CACH"}
@@ -1 +0,0 @@
1
- {"messages": [{"role": "system", "content": "你是一个翻译机器人"}, {"role": "user", "content": "将Reversal翻译成中文"}, {"role": "assistant", "content": "撤销"}]}