@yxw007/translate 0.1.6 → 0.2.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.
@@ -1,8 +1,11 @@
1
- // translate v0.1.6 Copyright (c) 2024 Potter<aa4790139@gmail.com> and contributors
1
+ // translate v0.2.2 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";
@@ -2379,15 +2445,20 @@ function getLanguage(engine) {
2379
2445
  }
2380
2446
 
2381
2447
  const appName = "Translate";
2448
+ const defaultMaxCharacterNum = 1000;
2382
2449
 
2383
2450
  const logger = useLogger();
2384
2451
  const cache = new Cache();
2385
2452
  class Translator {
2386
2453
  engines;
2387
2454
  cache_time;
2388
- constructor(cache_time = 60 * 1000) {
2455
+ concurrencyMax;
2456
+ concurrencyDelay;
2457
+ constructor(cache_time = 60 * 1000, concurrencyMax = 4, concurrencyDelay = 20) {
2389
2458
  this.engines = new Map();
2390
2459
  this.cache_time = cache_time;
2460
+ this.concurrencyMax = concurrencyMax;
2461
+ this.concurrencyDelay = concurrencyDelay;
2391
2462
  }
2392
2463
  /**
2393
2464
  * This method is obsolete, please use the addEngine method
@@ -2435,8 +2506,7 @@ class Translator {
2435
2506
  if (cache.get(key)) {
2436
2507
  return Promise.resolve(cache.get(key)?.value);
2437
2508
  }
2438
- return engineInstance
2439
- .translate(text, options)
2509
+ return this.concurrencyHandle(engineInstance, text, options)
2440
2510
  .then((translated) => {
2441
2511
  cache.set(key, translated, cache_time ?? this.cache_time);
2442
2512
  return translated;
@@ -2451,6 +2521,55 @@ class Translator {
2451
2521
  }
2452
2522
  });
2453
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
+ }
2454
2573
  }
2455
2574
  const translator = new Translator();
2456
2575
  var index = {