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 +93 -39
- package/index.d.ts +22 -11
- package/index.js +55 -36
- package/lib/utils.d.ts +19 -0
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +58 -0
- package/package.json +41 -23
- package/types/index.d.ts +18 -15
- package/types/index.d.ts.map +1 -1
- package/index.d.ts.map +0 -1
- package/src/fine-tuning/test.jsonl +0 -1
- package/src/index.ts +0 -365
- package/src/lib/cache/index.ts +0 -199
- package/src/lib/log/index.ts +0 -19
- package/src/lib/support.ts +0 -108
- package/src/lib/utils.ts +0 -90
- package/src/test/index.ts +0 -26
- package/src/types/index.ts +0 -145
- package/test/index.d.ts +0 -3
- package/test/index.d.ts.map +0 -1
- package/test/index.js +0 -32
- package/test.mjs +0 -16
- package/tsconfig.json +0 -121
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
|
-
|
|
14
|
-
npm install cc-translate -D
|
|
19
|
+
## Quick Start
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
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 {
|
|
3
|
-
|
|
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
|
|
6
|
-
/**
|
|
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
|
-
/**
|
|
17
|
-
openaiConfig: IOpenaiConfig;
|
|
19
|
+
/** 微调模型 */
|
|
18
20
|
fineTune: string[];
|
|
19
|
-
|
|
20
|
-
|
|
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 {
|
|
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
|
|
20
|
+
const DEFAULT_CHAT_COMPLETION_CREATE_PARAMS = {
|
|
22
21
|
model: "gpt-4o",
|
|
23
22
|
};
|
|
24
|
-
export class
|
|
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(`🚀
|
|
42
|
-
console.log(`🚀
|
|
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("🚀 ~
|
|
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
|
-
|
|
55
|
-
const
|
|
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
|
-
|
|
62
|
+
skippedNoContent.push(`${item.code}:${fileName}`);
|
|
64
63
|
continue;
|
|
65
64
|
}
|
|
66
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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(
|
|
153
|
+
throw new Error(`Unsupported language:${language}`);
|
|
146
154
|
}
|
|
147
155
|
if (!originLanguage) {
|
|
148
|
-
throw new Error(
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
255
|
-
this.SOURCE_LANGUAGE = (
|
|
265
|
+
/** 源语言 */
|
|
266
|
+
this.SOURCE_LANGUAGE = (_c = params.sourceLanguage) !== null && _c !== void 0 ? _c : "en";
|
|
267
|
+
/** 输出文件根路径 */
|
|
256
268
|
this.OUTPUT_ROOT_PATH = params.outputRootPath;
|
|
257
|
-
|
|
258
|
-
this.
|
|
259
|
-
|
|
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
|
package/lib/utils.d.ts.map
CHANGED
|
@@ -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.
|
|
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
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./index.d.ts",
|
|
15
|
+
"import": "./index.js",
|
|
16
|
+
"require": "./index.js",
|
|
17
|
+
"default": "./index.js"
|
|
18
|
+
}
|
|
12
19
|
},
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
20
|
+
"files": [
|
|
21
|
+
"index.js",
|
|
22
|
+
"index.d.ts",
|
|
23
|
+
"lib",
|
|
24
|
+
"types"
|
|
16
25
|
],
|
|
17
|
-
"
|
|
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":
|
|
24
|
-
|
|
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
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
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.
|
|
54
|
+
"@google/genai": "^1.43.0",
|
|
37
55
|
"ansi-colors": "^4.1.3",
|
|
38
56
|
"cli-progress": "^3.12.0",
|
|
39
|
-
"openai": "^
|
|
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
|
|
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
|
|
62
|
-
|
|
62
|
+
export interface II18nTranslateAgentParams {
|
|
63
|
+
/** openai 客户端配置 */
|
|
64
|
+
openaiClientConfig: ClientOptions;
|
|
65
|
+
/** openai 聊天完成创建参数 */
|
|
66
|
+
chatCompletionCreateParams?: Partial<ChatCompletionCreateParams>;
|
|
67
|
+
/** 缓存文件根路径 */
|
|
63
68
|
cacheFileRootPath: string;
|
|
64
|
-
/**
|
|
69
|
+
/** 入口文件根路径 */
|
|
65
70
|
fileRootPath: string;
|
|
66
|
-
|
|
71
|
+
/** 源语言 */
|
|
72
|
+
/** default en */
|
|
73
|
+
sourceLanguage?: SupportLanguageType;
|
|
74
|
+
/** 需要翻译的语言列表 */
|
|
67
75
|
languages: SupportLanguageType[];
|
|
76
|
+
/** 输出文件根路径 */
|
|
68
77
|
outputRootPath?: string;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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;
|
package/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,
|
|
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": "撤销"}]}
|