@yxw007/translate 0.1.5 → 0.2.0
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/dist/browser/index.cjs +135 -12
- package/dist/browser/index.cjs.map +1 -1
- package/dist/browser/index.esm.js +135 -12
- package/dist/browser/index.esm.js.map +1 -1
- package/dist/browser/index.esm.min.js +1 -1
- package/dist/browser/index.esm.min.js.map +1 -1
- package/dist/browser/index.min.cjs +1 -1
- package/dist/browser/index.min.cjs.map +1 -1
- package/dist/browser/index.umd.js +136 -16
- package/dist/browser/index.umd.js.map +1 -1
- package/dist/browser/index.umd.min.js +1 -1
- package/dist/browser/index.umd.min.js.map +1 -1
- package/dist/index.d.ts +10 -1
- package/dist/node/index.cjs +135 -12
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.js +135 -12
- package/dist/node/index.js.map +1 -1
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/dist/browser/index.cjs
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
// translate v0.
|
|
1
|
+
// translate v0.2.0 Copyright (c) 2025 Potter<aa4790139@gmail.com> and contributors
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
5
5
|
|
|
6
|
+
require('fs/promises');
|
|
7
|
+
require('fs');
|
|
8
|
+
require('path');
|
|
6
9
|
var clientTranslate = require('@aws-sdk/client-translate');
|
|
7
10
|
|
|
8
11
|
class TranslationError extends Error {
|
|
@@ -117,6 +120,9 @@ function useLogger(name = "") {
|
|
|
117
120
|
};
|
|
118
121
|
}
|
|
119
122
|
|
|
123
|
+
function sleep(ms) {
|
|
124
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
125
|
+
}
|
|
120
126
|
function getGapLine() {
|
|
121
127
|
return "-".repeat(20);
|
|
122
128
|
}
|
|
@@ -134,6 +140,66 @@ async function throwResponseError(name, res) {
|
|
|
134
140
|
catch (e) { }
|
|
135
141
|
return new TranslationError(name, `Translate fail ! ${res.status}: ${res.statusText} ${bodyRes?.message ?? ""}`);
|
|
136
142
|
}
|
|
143
|
+
function splitText(text, maxCharacterNum) {
|
|
144
|
+
const SPLIT_PRIORITY = [
|
|
145
|
+
/\n\n+/, // 段落分隔(优先保留空行)
|
|
146
|
+
/[.。!??!\n]/, // 中日韩句子结束符+英文标点+换行
|
|
147
|
+
/[;;]/, // 分号(中英文)
|
|
148
|
+
/[,,]/g, // 逗号(中英文)
|
|
149
|
+
/\s/, // 空格(避免切分单词)
|
|
150
|
+
];
|
|
151
|
+
const BEST_MATCH_RATIO = 0.7;
|
|
152
|
+
const chunks = [];
|
|
153
|
+
while (text.length > 0) {
|
|
154
|
+
const chunk = text.slice(0, maxCharacterNum);
|
|
155
|
+
// Scene 1:Prioritization of cases not subject to severance
|
|
156
|
+
if (text.length <= maxCharacterNum) {
|
|
157
|
+
chunks.push(text);
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
// Scene 2:Finding Split Points by Priority
|
|
161
|
+
let splitPos = -1;
|
|
162
|
+
for (const delimiter of SPLIT_PRIORITY) {
|
|
163
|
+
const regex = new RegExp(delimiter.source + "(?=[^]*)", "g"); // back-to-front search
|
|
164
|
+
let m, longestMatch;
|
|
165
|
+
while ((m = regex.exec(chunk)) !== null) {
|
|
166
|
+
if (m.index === regex.lastIndex) {
|
|
167
|
+
regex.lastIndex++;
|
|
168
|
+
}
|
|
169
|
+
if (longestMatch != null) {
|
|
170
|
+
longestMatch = m.index > longestMatch.index ? m : longestMatch;
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
longestMatch = m;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (longestMatch?.index !== undefined && longestMatch.index >= maxCharacterNum * BEST_MATCH_RATIO) {
|
|
177
|
+
splitPos = longestMatch.index;
|
|
178
|
+
break; // Finding Quality Split Points
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Scene 3:Conservative splitting in the absence of a suitable separator
|
|
182
|
+
if (splitPos === -1) {
|
|
183
|
+
splitPos = chunk.lastIndexOf(" ", maxCharacterNum); // look for the space
|
|
184
|
+
splitPos = splitPos === -1 ? maxCharacterNum : splitPos; // forcible division
|
|
185
|
+
}
|
|
186
|
+
if (splitPos == 0) {
|
|
187
|
+
text = text.slice(splitPos + 1);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
chunks.push(text.slice(0, splitPos));
|
|
191
|
+
text = text.slice(splitPos);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return chunks;
|
|
195
|
+
}
|
|
196
|
+
function isOverMaxCharacterNum(text, max_character_num) {
|
|
197
|
+
if (!text || text.length <= 0) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
const total = text.reduce((pre, cur) => pre + cur.length, 0);
|
|
201
|
+
return total > max_character_num;
|
|
202
|
+
}
|
|
137
203
|
|
|
138
204
|
function google(options) {
|
|
139
205
|
const base = "https://translate.googleapis.com/translate_a/single";
|
|
@@ -1450,25 +1516,29 @@ function deepl$2(options) {
|
|
|
1450
1516
|
if (!Array.isArray(text)) {
|
|
1451
1517
|
text = [text];
|
|
1452
1518
|
}
|
|
1453
|
-
const requestBody = JSON.stringify({
|
|
1454
|
-
text,
|
|
1455
|
-
source_lang: from === "auto" ? undefined : from,
|
|
1456
|
-
target_lang: to,
|
|
1457
|
-
});
|
|
1458
1519
|
const res = await fetch(url, {
|
|
1459
1520
|
method: "POST",
|
|
1460
1521
|
headers: {
|
|
1461
|
-
"Content-Type": "application/json; charset=UTF-8
|
|
1522
|
+
"Content-Type": "application/json; charset=UTF-8",
|
|
1462
1523
|
Authorization: `DeepL-Auth-Key ${key}`,
|
|
1524
|
+
Accept: "*/*",
|
|
1525
|
+
Host: "api-free.deepl.com",
|
|
1463
1526
|
Connection: "keep-alive",
|
|
1464
1527
|
},
|
|
1465
|
-
body:
|
|
1528
|
+
body: JSON.stringify({
|
|
1529
|
+
text: text,
|
|
1530
|
+
source_lang: from === "auto" ? undefined : from,
|
|
1531
|
+
target_lang: to,
|
|
1532
|
+
}),
|
|
1466
1533
|
});
|
|
1467
1534
|
if (!res.ok) {
|
|
1468
1535
|
throw await throwResponseError(this.name, res);
|
|
1469
1536
|
}
|
|
1470
1537
|
const bodyRes = await res.json();
|
|
1471
|
-
|
|
1538
|
+
if (bodyRes.error) {
|
|
1539
|
+
throw new TranslationError(this.name, `Translate fail ! code: ${bodyRes.error.code}, message: ${bodyRes.error.message}`);
|
|
1540
|
+
}
|
|
1541
|
+
const body = bodyRes.translations;
|
|
1472
1542
|
if (!body || body.length === 0) {
|
|
1473
1543
|
throw new TranslationError(this.name, "Translate fail ! translate's result is null or empty");
|
|
1474
1544
|
}
|
|
@@ -2375,15 +2445,20 @@ function getLanguage(engine) {
|
|
|
2375
2445
|
}
|
|
2376
2446
|
|
|
2377
2447
|
const appName = "Translate";
|
|
2448
|
+
const defaultMaxCharacterNum = 1000;
|
|
2378
2449
|
|
|
2379
2450
|
const logger = useLogger();
|
|
2380
2451
|
const cache = new Cache();
|
|
2381
2452
|
class Translator {
|
|
2382
2453
|
engines;
|
|
2383
2454
|
cache_time;
|
|
2384
|
-
|
|
2455
|
+
concurrencyMax;
|
|
2456
|
+
concurrencyDelay;
|
|
2457
|
+
constructor(cache_time = 60 * 1000, concurrencyMax = 4, concurrencyDelay = 20) {
|
|
2385
2458
|
this.engines = new Map();
|
|
2386
2459
|
this.cache_time = cache_time;
|
|
2460
|
+
this.concurrencyMax = concurrencyMax;
|
|
2461
|
+
this.concurrencyDelay = concurrencyDelay;
|
|
2387
2462
|
}
|
|
2388
2463
|
/**
|
|
2389
2464
|
* This method is obsolete, please use the addEngine method
|
|
@@ -2431,8 +2506,7 @@ class Translator {
|
|
|
2431
2506
|
if (cache.get(key)) {
|
|
2432
2507
|
return Promise.resolve(cache.get(key)?.value);
|
|
2433
2508
|
}
|
|
2434
|
-
return engineInstance
|
|
2435
|
-
.translate(text, options)
|
|
2509
|
+
return this.concurrencyHandle(engineInstance, text, options)
|
|
2436
2510
|
.then((translated) => {
|
|
2437
2511
|
cache.set(key, translated, cache_time ?? this.cache_time);
|
|
2438
2512
|
return translated;
|
|
@@ -2447,6 +2521,55 @@ class Translator {
|
|
|
2447
2521
|
}
|
|
2448
2522
|
});
|
|
2449
2523
|
}
|
|
2524
|
+
async concurrencyHandle(engine, text, options) {
|
|
2525
|
+
const { max_character_num = defaultMaxCharacterNum } = options;
|
|
2526
|
+
const maxCharacterNum = max_character_num > 0 ? max_character_num : defaultMaxCharacterNum;
|
|
2527
|
+
if (Array.isArray(text)) {
|
|
2528
|
+
if (isOverMaxCharacterNum(text, max_character_num)) {
|
|
2529
|
+
throw new TranslationError(appName, "String arrays do not support automatic character splitting, and the total number of characters in a string array exceeds the limit on the number of translated characters.");
|
|
2530
|
+
}
|
|
2531
|
+
return engine.translate(text, options);
|
|
2532
|
+
}
|
|
2533
|
+
else {
|
|
2534
|
+
return this.concurrencyTranslate(engine, text, options, maxCharacterNum);
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
async concurrencyTranslate(engine, text, options, maxCharacterMum) {
|
|
2538
|
+
const pendingTasks = splitText(text, maxCharacterMum).map((content, index) => ({ content, index }));
|
|
2539
|
+
const result = [];
|
|
2540
|
+
let activeTasks = 0;
|
|
2541
|
+
const concurrencyDelay = this.concurrencyDelay;
|
|
2542
|
+
const concurrencyMax = this.concurrencyMax;
|
|
2543
|
+
return new Promise((resolve, reject) => {
|
|
2544
|
+
function processTasks() {
|
|
2545
|
+
while (activeTasks < concurrencyMax && pendingTasks.length > 0) {
|
|
2546
|
+
const { content, index } = pendingTasks.shift();
|
|
2547
|
+
activeTasks++;
|
|
2548
|
+
engine
|
|
2549
|
+
.translate(content, options)
|
|
2550
|
+
.then((res) => {
|
|
2551
|
+
result.push({
|
|
2552
|
+
translated: res,
|
|
2553
|
+
index,
|
|
2554
|
+
});
|
|
2555
|
+
})
|
|
2556
|
+
.catch((error) => reject(error))
|
|
2557
|
+
.finally(async () => {
|
|
2558
|
+
activeTasks--;
|
|
2559
|
+
if (activeTasks === 0 && pendingTasks.length <= 0) {
|
|
2560
|
+
result.sort((a, b) => a.index - b.index);
|
|
2561
|
+
const arr = result.reduce((pre, cur) => pre.concat(cur.translated), []);
|
|
2562
|
+
return resolve([arr.join("")]);
|
|
2563
|
+
}
|
|
2564
|
+
await sleep(concurrencyDelay);
|
|
2565
|
+
processTasks();
|
|
2566
|
+
});
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
processTasks();
|
|
2570
|
+
return result;
|
|
2571
|
+
});
|
|
2572
|
+
}
|
|
2450
2573
|
}
|
|
2451
2574
|
const translator = new Translator();
|
|
2452
2575
|
var index = {
|