@nocobase/plugin-localization 2.1.0-beta.34 → 2.1.0-beta.36

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.
@@ -27,21 +27,22 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
27
27
  var localization_ai_translate_exports = {};
28
28
  __export(localization_ai_translate_exports, {
29
29
  LOCALIZATION_AI_TRANSLATE_TASK_TYPE: () => LOCALIZATION_AI_TRANSLATE_TASK_TYPE,
30
- LocalizationAITranslateTask: () => LocalizationAITranslateTask
30
+ LocalizationAITranslateTask: () => LocalizationAITranslateTask,
31
+ pickBuiltInResourceReference: () => pickBuiltInResourceReference
31
32
  });
32
33
  module.exports = __toCommonJS(localization_ai_translate_exports);
33
34
  var import_plugin_async_task_manager = require("@nocobase/plugin-async-task-manager");
34
35
  var import_messages = require("@langchain/core/messages");
36
+ var import_translation_scope = require("../translation-scope");
35
37
  const LOCALIZATION_AI_TRANSLATE_TASK_TYPE = "localization:ai-translate";
36
- const TRANSLATION_BATCH_SIZE = 10;
37
38
  const DEFAULT_TRANSLATION_WORKER_COUNT = 10;
38
39
  const MIN_TRANSLATION_WORKER_COUNT = 1;
39
40
  const MAX_TRANSLATION_WORKER_COUNT = 20;
40
- const MAX_TRANSLATION_QUEUE_SIZE = TRANSLATION_BATCH_SIZE * 2;
41
+ const TRANSLATION_CHUNK_SIZE = 200;
41
42
  const elapsed = (start) => Date.now() - start;
42
- const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
43
43
  const truncateForLog = (value, maxLength = 500) => value.length > maxLength ? `${value.slice(0, maxLength)}...` : value;
44
44
  const LEGACY_SYMBOL_TRANSLATIONS = /* @__PURE__ */ new Set(["<", "=", ">"]);
45
+ const isString = (value) => typeof value === "string" && value.length > 0;
45
46
  const getTranslationWorkerCount = () => {
46
47
  const value = Number.parseInt(process.env.AI_LOCALIZATION_CONCURRENCY || "", 10);
47
48
  if (Number.isInteger(value) && value >= MIN_TRANSLATION_WORKER_COUNT && value <= MAX_TRANSLATION_WORKER_COUNT) {
@@ -49,6 +50,16 @@ const getTranslationWorkerCount = () => {
49
50
  }
50
51
  return DEFAULT_TRANSLATION_WORKER_COUNT;
51
52
  };
53
+ const pickBuiltInResourceReference = (row, references, locales) => {
54
+ var _a;
55
+ for (const locale of [locales.primary, locales.fallback].filter(isString)) {
56
+ const translation = (_a = references.get(locale)) == null ? void 0 : _a.get(String(row.id));
57
+ if (translation) {
58
+ return { locale, translation };
59
+ }
60
+ }
61
+ return {};
62
+ };
52
63
  class LocalizationAITranslationError extends Error {
53
64
  constructor(message, details) {
54
65
  super(message);
@@ -74,11 +85,21 @@ class LocalizationAITranslateTask extends import_plugin_async_task_manager.TaskT
74
85
  const resolvedModel = await aiPlugin.aiEmployeesManager.resolveModel(employee, params.model);
75
86
  const { provider, model, service } = await aiPlugin.aiManager.getLLMService(resolvedModel);
76
87
  const defaultReferenceLocale = await this.getSystemDefaultLocale();
77
- const builtInReferenceResources = await this.app.localeManager.getBuiltInResources("zh-CN");
78
88
  const builtInMatchResources = await this.app.localeManager.getBuiltInResources("en-US");
89
+ const referenceLocales = this.resolveReferenceLocales(params.referenceLocales, defaultReferenceLocale);
90
+ const findTextsOptions = await (0, import_translation_scope.buildFindTextsOptions)({
91
+ app: this.app,
92
+ mode: params.mode,
93
+ locale,
94
+ scope: params.scope || "all",
95
+ textIds: params.textIds,
96
+ fields: ["id", "text", "module"],
97
+ sort: ["id"]
98
+ });
79
99
  const workerCount = getTranslationWorkerCount();
100
+ const chunkSize = TRANSLATION_CHUNK_SIZE;
80
101
  const countStart = Date.now();
81
- const total = await this.countTexts(params.mode, locale, params.textIds);
102
+ const total = await this.countTexts(findTextsOptions);
82
103
  (_a = this.logger) == null ? void 0 : _a.debug("Localization AI translation task started", {
83
104
  taskId: this.record.id,
84
105
  mode: params.mode,
@@ -86,96 +107,86 @@ class LocalizationAITranslateTask extends import_plugin_async_task_manager.TaskT
86
107
  total,
87
108
  countElapsedMs: elapsed(countStart),
88
109
  workerCount,
89
- queueLimit: MAX_TRANSLATION_QUEUE_SIZE,
110
+ chunkSize,
90
111
  defaultReferenceLocale,
112
+ referenceLocales,
113
+ scope: params.scope || "all",
91
114
  provider: service == null ? void 0 : service.provider,
92
115
  llmService: service == null ? void 0 : service.name,
93
116
  model
94
117
  });
95
118
  let translated = 0;
96
119
  let chunkIndex = 0;
97
- let producerDone = false;
98
- let firstError;
99
- const queue = [];
100
120
  this.reportProgress({ total, current: 0 });
101
- const workers = Array.from(
102
- { length: workerCount },
103
- (_, workerIndex) => this.runTranslationWorker({
104
- workerIndex: workerIndex + 1,
105
- queue,
106
- isDone: () => producerDone,
107
- getError: () => firstError,
108
- setError: (error) => {
109
- firstError = firstError ?? error;
110
- },
111
- total,
112
- getTranslated: () => translated,
113
- incrementTranslated: () => {
114
- translated += 1;
115
- return translated;
116
- },
117
- locale,
118
- employeeUsername,
119
- employee,
120
- provider,
121
- service,
122
- model
123
- })
124
- );
125
- try {
126
- await this.app.db.getRepository("localizationTexts").chunkWithCursor({
127
- ...this.buildFindTextsOptions(params.mode, locale, params.textIds),
128
- chunkSize: TRANSLATION_BATCH_SIZE,
129
- beforeFind: async () => {
130
- while (!firstError && queue.length >= MAX_TRANSLATION_QUEUE_SIZE) {
131
- await sleep(100);
132
- }
133
- if (firstError) {
134
- throw firstError;
121
+ const repository = this.app.db.getRepository("localizationTexts");
122
+ const chunk = params.mode === "selected" ? repository.chunk.bind(repository) : repository.chunkWithCursor.bind(repository);
123
+ await chunk({
124
+ ...findTextsOptions,
125
+ chunkSize,
126
+ callback: async (rows) => {
127
+ var _a2, _b2, _c;
128
+ chunkIndex += 1;
129
+ const chunkStart = Date.now();
130
+ const textRows = rows.map((row) => (0, import_translation_scope.normalizeTextRecord)(row)).filter(Boolean);
131
+ const rowsWithScope = textRows.map((row) => ({
132
+ row,
133
+ isBuiltIn: (0, import_translation_scope.isBuiltInText)(row, builtInMatchResources)
134
+ }));
135
+ (_a2 = this.logger) == null ? void 0 : _a2.debug("Localization AI translation chunk loaded", {
136
+ taskId: this.record.id,
137
+ chunkIndex,
138
+ rows: textRows.length,
139
+ workerCount,
140
+ translated,
141
+ total
142
+ });
143
+ for (let start = 0; start < rowsWithScope.length; start += workerCount) {
144
+ if (this.isCanceled) {
145
+ throw new import_plugin_async_task_manager.CancelError();
135
146
  }
136
- },
137
- callback: async (rows) => {
138
- var _a2;
139
- chunkIndex += 1;
140
- const chunkStart = Date.now();
141
- const textRows = rows.map((row) => this.normalizeTextRecord(row)).filter(Boolean);
142
- const textIds = textRows.map((row) => row.id);
143
- const englishReferences = await this.getLocaleReferences(textIds, "en-US");
144
- const defaultLocaleReferences = defaultReferenceLocale === "en-US" ? englishReferences : await this.getLocaleReferences(textIds, defaultReferenceLocale);
145
- const queueItems = textRows.map((row) => {
146
- const isBuiltIn = this.isBuiltInText(row, builtInMatchResources);
147
- return {
148
- row,
149
- chunkIndex,
150
- englishReference: englishReferences.get(String(row.id)),
151
- referenceTranslation: isBuiltIn ? this.getBuiltInReference(row, builtInReferenceResources) : defaultLocaleReferences.get(String(row.id)),
152
- referenceLocale: isBuiltIn ? "zh-CN" : defaultReferenceLocale,
153
- isBuiltIn
154
- };
155
- });
156
- queue.push(...queueItems);
157
- (_a2 = this.logger) == null ? void 0 : _a2.debug("Localization AI translation chunk enqueued", {
147
+ const batchStart = Date.now();
148
+ const batch = rowsWithScope.slice(start, start + workerCount);
149
+ const items = await this.buildTranslationItems(batch, referenceLocales, chunkIndex);
150
+ await Promise.all(
151
+ items.map(
152
+ (item, index) => this.translateItem({
153
+ workerIndex: index + 1,
154
+ item,
155
+ total,
156
+ getTranslated: () => translated,
157
+ incrementTranslated: () => {
158
+ translated += 1;
159
+ return translated;
160
+ },
161
+ locale,
162
+ employeeUsername,
163
+ employee,
164
+ provider,
165
+ service,
166
+ model
167
+ })
168
+ )
169
+ );
170
+ (_b2 = this.logger) == null ? void 0 : _b2.debug("Localization AI translation batch completed", {
158
171
  taskId: this.record.id,
159
172
  chunkIndex,
160
- rows: textRows.length,
161
- englishReferences: englishReferences.size,
162
- referenceLocale: defaultReferenceLocale,
163
- referenceTranslations: queueItems.filter((item) => item.referenceTranslation).length,
164
- builtInReferences: queueItems.filter((item) => item.isBuiltIn && item.referenceTranslation).length,
165
- queueSize: queue.length,
166
- elapsedMs: elapsed(chunkStart),
173
+ batchSize: items.length,
174
+ referenceTranslations: items.filter((item) => item.referenceTranslation).length,
175
+ elapsedMs: elapsed(batchStart),
167
176
  translated,
168
177
  total
169
178
  });
170
179
  }
171
- });
172
- } finally {
173
- producerDone = true;
174
- }
175
- await Promise.all(workers);
176
- if (firstError) {
177
- throw firstError;
178
- }
180
+ (_c = this.logger) == null ? void 0 : _c.debug("Localization AI translation chunk completed", {
181
+ taskId: this.record.id,
182
+ chunkIndex,
183
+ rows: textRows.length,
184
+ elapsedMs: elapsed(chunkStart),
185
+ translated,
186
+ total
187
+ });
188
+ }
189
+ });
179
190
  (_b = this.logger) == null ? void 0 : _b.debug("Localization AI translation task completed", {
180
191
  taskId: this.record.id,
181
192
  translated,
@@ -186,34 +197,8 @@ class LocalizationAITranslateTask extends import_plugin_async_task_manager.TaskT
186
197
  total
187
198
  };
188
199
  }
189
- async countTexts(mode, locale, textIds) {
190
- return await this.app.db.getRepository("localizationTexts").count(this.buildFindTextsOptions(mode, locale, textIds));
191
- }
192
- buildFindTextsOptions(mode, locale, textIds) {
193
- const options = {
194
- fields: ["id", "text", "module"],
195
- sort: ["id"]
196
- };
197
- if (mode === "selected") {
198
- options.filter = {
199
- id: {
200
- $in: textIds || []
201
- }
202
- };
203
- }
204
- if (mode === "incremental") {
205
- options.include = [{ association: "translations", where: { locale }, required: false }];
206
- options.where = {
207
- "$translations.id$": null
208
- };
209
- }
210
- return options;
211
- }
212
- normalizeTextRecord(row) {
213
- if (!row) {
214
- return void 0;
215
- }
216
- return typeof row.toJSON === "function" ? row.toJSON() : row;
200
+ async countTexts(options) {
201
+ return await this.app.db.getRepository("localizationTexts").count(options);
217
202
  }
218
203
  async getLocaleReferences(textIds, locale) {
219
204
  const references = /* @__PURE__ */ new Map();
@@ -223,10 +208,10 @@ class LocalizationAITranslateTask extends import_plugin_async_task_manager.TaskT
223
208
  const rows = await this.app.db.getRepository("localizationTranslations").find({
224
209
  fields: ["textId", "translation"],
225
210
  filter: {
226
- locale,
227
211
  textId: {
228
212
  $in: textIds
229
- }
213
+ },
214
+ locale
230
215
  }
231
216
  });
232
217
  for (const row of rows) {
@@ -237,34 +222,101 @@ class LocalizationAITranslateTask extends import_plugin_async_task_manager.TaskT
237
222
  }
238
223
  return references;
239
224
  }
225
+ async getReferenceMaps(textIds, locales) {
226
+ const result = /* @__PURE__ */ new Map();
227
+ await Promise.all(
228
+ Array.from(new Set(locales)).map(async (locale) => {
229
+ result.set(locale, await this.getLocaleReferences(textIds, locale));
230
+ })
231
+ );
232
+ return result;
233
+ }
234
+ async getBuiltInReferenceMaps(rows, locales) {
235
+ const result = /* @__PURE__ */ new Map();
236
+ if (!rows.length) {
237
+ return result;
238
+ }
239
+ await Promise.all(
240
+ Array.from(new Set(locales)).map(async (locale) => {
241
+ var _a;
242
+ const resources = await this.app.localeManager.getCacheResources(locale);
243
+ const references = /* @__PURE__ */ new Map();
244
+ for (const row of rows) {
245
+ const moduleName = (0, import_translation_scope.getModuleName)(row);
246
+ if (!moduleName) {
247
+ continue;
248
+ }
249
+ const modules = Array.from(/* @__PURE__ */ new Set([(0, import_translation_scope.normalizeModuleName)(moduleName), moduleName]));
250
+ for (const module2 of modules) {
251
+ const translation = (_a = resources == null ? void 0 : resources[module2]) == null ? void 0 : _a[row.text];
252
+ if (translation) {
253
+ references.set(String(row.id), translation);
254
+ break;
255
+ }
256
+ }
257
+ }
258
+ result.set(locale, references);
259
+ })
260
+ );
261
+ return result;
262
+ }
263
+ pickDbReference(row, references, locales) {
264
+ var _a;
265
+ for (const locale of [locales.primary, locales.fallback].filter(isString)) {
266
+ const translation = (_a = references.get(locale)) == null ? void 0 : _a.get(String(row.id));
267
+ if (translation) {
268
+ return { locale, translation };
269
+ }
270
+ }
271
+ return {};
272
+ }
240
273
  async getSystemDefaultLocale() {
241
274
  var _a;
242
275
  const systemSetting = await ((_a = this.app.db.getRepository("systemSettings")) == null ? void 0 : _a.findOne());
243
276
  const enabledLanguages = (systemSetting == null ? void 0 : systemSetting.get("enabledLanguages")) || [];
244
277
  return (enabledLanguages == null ? void 0 : enabledLanguages[0]) || process.env.APP_LANG || "en-US";
245
278
  }
246
- getModuleName(row) {
247
- var _a;
248
- return (_a = row.module) == null ? void 0 : _a.replace("resources.", "");
249
- }
250
- isBuiltInText(row, resources) {
251
- var _a;
252
- const moduleName = this.getModuleName(row);
253
- return Boolean(moduleName && ((_a = resources[moduleName]) == null ? void 0 : _a[row.text]) !== void 0);
279
+ resolveReferenceLocales(referenceLocales, defaultReferenceLocale) {
280
+ var _a, _b, _c, _d;
281
+ return {
282
+ builtIn: {
283
+ primary: ((_a = referenceLocales == null ? void 0 : referenceLocales.builtIn) == null ? void 0 : _a.primary) || "zh-CN",
284
+ fallback: ((_b = referenceLocales == null ? void 0 : referenceLocales.builtIn) == null ? void 0 : _b.fallback) || "ja-JP"
285
+ },
286
+ custom: {
287
+ primary: ((_c = referenceLocales == null ? void 0 : referenceLocales.custom) == null ? void 0 : _c.primary) || defaultReferenceLocale,
288
+ fallback: ((_d = referenceLocales == null ? void 0 : referenceLocales.custom) == null ? void 0 : _d.fallback) || "zh-CN"
289
+ }
290
+ };
254
291
  }
255
- getBuiltInReference(row, resources) {
256
- var _a;
257
- const moduleName = this.getModuleName(row);
258
- return moduleName ? (_a = resources[moduleName]) == null ? void 0 : _a[row.text] : void 0;
292
+ async buildTranslationItems(batch, referenceLocales, chunkIndex) {
293
+ const builtInRows = batch.filter((item) => item.isBuiltIn).map((item) => item.row);
294
+ const customTextIds = batch.filter((item) => !item.isBuiltIn).map((item) => item.row.id);
295
+ const builtInReferences = await this.getBuiltInReferenceMaps(
296
+ builtInRows,
297
+ [referenceLocales.builtIn.primary, referenceLocales.builtIn.fallback].filter(isString)
298
+ );
299
+ const customReferences = await this.getReferenceMaps(
300
+ customTextIds,
301
+ [referenceLocales.custom.primary, referenceLocales.custom.fallback].filter(isString)
302
+ );
303
+ return batch.map(({ row, isBuiltIn }) => {
304
+ const references = isBuiltIn ? referenceLocales.builtIn : referenceLocales.custom;
305
+ const reference = isBuiltIn ? pickBuiltInResourceReference(row, builtInReferences, references) : this.pickDbReference(row, customReferences, references);
306
+ return {
307
+ row,
308
+ chunkIndex,
309
+ referenceTranslation: reference.translation,
310
+ referenceLocale: reference.locale,
311
+ isBuiltIn
312
+ };
313
+ });
259
314
  }
260
- async runTranslationWorker(options) {
315
+ async translateItem(options) {
261
316
  var _a, _b, _c, _d, _e, _f, _g, _h;
262
317
  const {
263
318
  workerIndex,
264
- queue,
265
- isDone,
266
- getError,
267
- setError,
319
+ item,
268
320
  total,
269
321
  getTranslated,
270
322
  incrementTranslated,
@@ -275,102 +327,85 @@ class LocalizationAITranslateTask extends import_plugin_async_task_manager.TaskT
275
327
  service,
276
328
  model
277
329
  } = options;
278
- while (!isDone() || queue.length > 0) {
279
- if (getError()) {
280
- return;
281
- }
282
- if (this.isCanceled) {
283
- throw new import_plugin_async_task_manager.CancelError();
284
- }
285
- const item = queue.shift();
286
- if (!item) {
287
- await sleep(50);
288
- continue;
289
- }
290
- const { row, chunkIndex, englishReference, referenceTranslation, referenceLocale, isBuiltIn } = item;
291
- try {
292
- const textStart = Date.now();
293
- (_c = (_a = this.logger) == null ? void 0 : _a.trace) == null ? void 0 : _c.call(_a, "Localization AI translation text started", {
330
+ if (this.isCanceled) {
331
+ throw new import_plugin_async_task_manager.CancelError();
332
+ }
333
+ const { row, chunkIndex, referenceTranslation, referenceLocale, isBuiltIn } = item;
334
+ try {
335
+ const textStart = Date.now();
336
+ (_c = (_a = this.logger) == null ? void 0 : _a.trace) == null ? void 0 : _c.call(_a, "Localization AI translation text started", {
337
+ taskId: this.record.id,
338
+ workerIndex,
339
+ chunkIndex,
340
+ textId: row.id,
341
+ textLength: ((_b = row.text) == null ? void 0 : _b.length) ?? 0,
342
+ hasReferenceTranslation: Boolean(referenceTranslation),
343
+ referenceLocale,
344
+ isBuiltIn,
345
+ translated: getTranslated(),
346
+ total
347
+ });
348
+ const isLegacySymbolTranslation = LEGACY_SYMBOL_TRANSLATIONS.has(row.text);
349
+ if (isLegacySymbolTranslation) {
350
+ (_e = (_d = this.logger) == null ? void 0 : _d.trace) == null ? void 0 : _e.call(_d, "Localization AI translation legacy symbol skipped", {
294
351
  taskId: this.record.id,
295
352
  workerIndex,
296
353
  chunkIndex,
297
354
  textId: row.id,
298
- textLength: ((_b = row.text) == null ? void 0 : _b.length) ?? 0,
299
- hasEnglishReference: Boolean(englishReference),
300
- hasReferenceTranslation: Boolean(referenceTranslation),
301
- referenceLocale,
302
- isBuiltIn,
303
- queueSize: queue.length,
304
- translated: getTranslated(),
305
- total
306
- });
307
- const isLegacySymbolTranslation = LEGACY_SYMBOL_TRANSLATIONS.has(row.text);
308
- if (isLegacySymbolTranslation) {
309
- (_e = (_d = this.logger) == null ? void 0 : _d.trace) == null ? void 0 : _e.call(_d, "Localization AI translation legacy symbol skipped", {
310
- taskId: this.record.id,
311
- workerIndex,
312
- chunkIndex,
313
- textId: row.id,
314
- text: row.text
315
- });
316
- }
317
- const translation = isLegacySymbolTranslation ? row.text : await this.translateText({
318
- text: row.text,
319
- module: row.module,
320
- englishReference,
321
- referenceTranslation,
322
- referenceLocale,
323
- isBuiltIn,
324
- locale,
325
- employeeUsername,
326
- employee,
327
- provider,
328
- service,
329
- model
330
- });
331
- const aiElapsedMs = elapsed(textStart);
332
- const writeStart = Date.now();
333
- await this.app.db.getRepository("localizationTranslations").updateOrCreate({
334
- filterKeys: ["textId", "locale"],
335
- values: {
336
- textId: row.id,
337
- locale,
338
- translation
339
- }
355
+ text: row.text
340
356
  });
341
- const writeElapsedMs = elapsed(writeStart);
342
- const translated = incrementTranslated();
343
- this.reportProgress({ total, current: translated });
344
- (_g = this.logger) == null ? void 0 : _g.debug("Localization AI translation text completed", {
345
- taskId: this.record.id,
346
- workerIndex,
347
- chunkIndex,
357
+ }
358
+ const translation = isLegacySymbolTranslation ? row.text : await this.translateText({
359
+ text: row.text,
360
+ module: row.module,
361
+ referenceTranslation,
362
+ referenceLocale,
363
+ isBuiltIn,
364
+ locale,
365
+ employeeUsername,
366
+ employee,
367
+ provider,
368
+ service,
369
+ model
370
+ });
371
+ const aiElapsedMs = elapsed(textStart);
372
+ const writeStart = Date.now();
373
+ await this.app.db.getRepository("localizationTranslations").updateOrCreate({
374
+ filterKeys: ["textId", "locale"],
375
+ values: {
348
376
  textId: row.id,
349
- textLength: ((_f = row.text) == null ? void 0 : _f.length) ?? 0,
350
- translationLength: translation.length,
351
- aiElapsedMs,
352
- writeElapsedMs,
353
- totalElapsedMs: elapsed(textStart),
354
- translated,
355
- total,
356
- queueSize: queue.length
357
- });
358
- } catch (error) {
359
- const message = error instanceof Error ? error.message : String(error);
360
- const details = {
361
- id: row.id,
362
- text: row.text,
363
- error: message
364
- };
365
- (_h = this.logger) == null ? void 0 : _h.error(`Failed to translate localization text ${row.id}: ${message}`, { error });
366
- if (error instanceof import_plugin_async_task_manager.CancelError) {
367
- throw error;
377
+ locale,
378
+ translation
368
379
  }
369
- setError(
370
- new LocalizationAITranslationError(`Failed to translate localization text ${row.id}: ${message}`, details)
371
- );
372
- return;
380
+ });
381
+ const writeElapsedMs = elapsed(writeStart);
382
+ const translated = incrementTranslated();
383
+ this.reportProgress({ total, current: translated });
384
+ (_g = this.logger) == null ? void 0 : _g.debug("Localization AI translation text completed", {
385
+ taskId: this.record.id,
386
+ workerIndex,
387
+ chunkIndex,
388
+ textId: row.id,
389
+ textLength: ((_f = row.text) == null ? void 0 : _f.length) ?? 0,
390
+ translationLength: translation.length,
391
+ aiElapsedMs,
392
+ writeElapsedMs,
393
+ totalElapsedMs: elapsed(textStart),
394
+ translated,
395
+ total
396
+ });
397
+ } catch (error) {
398
+ const message = error instanceof Error ? error.message : String(error);
399
+ const details = {
400
+ id: row.id,
401
+ text: row.text,
402
+ error: message
403
+ };
404
+ (_h = this.logger) == null ? void 0 : _h.error(`Failed to translate localization text ${row.id}: ${message}`, { error });
405
+ if (error instanceof import_plugin_async_task_manager.CancelError) {
406
+ throw error;
373
407
  }
408
+ throw new LocalizationAITranslationError(`Failed to translate localization text ${row.id}: ${message}`, details);
374
409
  }
375
410
  }
376
411
  async translateText(options) {
@@ -378,7 +413,6 @@ class LocalizationAITranslateTask extends import_plugin_async_task_manager.TaskT
378
413
  const {
379
414
  text,
380
415
  module: module2,
381
- englishReference,
382
416
  referenceTranslation,
383
417
  referenceLocale,
384
418
  isBuiltIn,
@@ -390,14 +424,13 @@ class LocalizationAITranslateTask extends import_plugin_async_task_manager.TaskT
390
424
  employee
391
425
  } = options;
392
426
  const setupStart = Date.now();
393
- const sourceText = englishReference || text;
394
- const sourceLang = englishReference ? "English" : "auto";
427
+ const sourceText = text;
395
428
  const targetLang = this.getLanguageName(locale);
396
429
  const context = this.buildProviderContext({
430
+ systemPrompt: this.getEmployeeSystemPrompt(employee),
397
431
  sourceText,
398
432
  targetLang,
399
- referenceSourceTerm: sourceText,
400
- referenceTargetTerm: referenceTranslation
433
+ referenceTranslation
401
434
  });
402
435
  const invokeStart = Date.now();
403
436
  (_b = (_a = this.logger) == null ? void 0 : _a.trace) == null ? void 0 : _b.call(_a, "Localization AI translation invoke started", {
@@ -405,44 +438,29 @@ class LocalizationAITranslateTask extends import_plugin_async_task_manager.TaskT
405
438
  textLength: (text == null ? void 0 : text.length) ?? 0,
406
439
  sourceTextLength: (sourceText == null ? void 0 : sourceText.length) ?? 0,
407
440
  locale,
408
- sourceLang,
409
441
  targetLang,
410
442
  module: module2,
411
443
  employeeUsername,
412
444
  provider: service == null ? void 0 : service.provider,
413
445
  llmService: service == null ? void 0 : service.name,
414
446
  model,
415
- hasEnglishReference: Boolean(englishReference),
416
447
  hasReferenceTranslation: Boolean(referenceTranslation),
417
448
  referenceLocale,
418
449
  isBuiltIn
419
450
  });
420
- const result = await provider.invoke(context, {
421
- modelRequestParams: {
422
- sourceText,
423
- sourceLang,
424
- targetLang,
425
- terms: this.buildTranslationTerms({
426
- sourceTerm: sourceText,
427
- targetTerm: referenceTranslation,
428
- targetLang
429
- })
430
- }
431
- });
451
+ const result = await provider.invoke(context);
432
452
  const invokeElapsedMs = elapsed(invokeStart);
433
453
  (_d = (_c = this.logger) == null ? void 0 : _c.trace) == null ? void 0 : _d.call(_c, "Localization AI translation invoke completed", {
434
454
  taskId: this.record.id,
435
455
  textLength: (text == null ? void 0 : text.length) ?? 0,
436
456
  sourceTextLength: (sourceText == null ? void 0 : sourceText.length) ?? 0,
437
457
  locale,
438
- sourceLang,
439
458
  targetLang,
440
459
  module: module2,
441
460
  employeeUsername,
442
461
  provider: service == null ? void 0 : service.provider,
443
462
  llmService: service == null ? void 0 : service.name,
444
463
  model,
445
- hasEnglishReference: Boolean(englishReference),
446
464
  hasReferenceTranslation: Boolean(referenceTranslation),
447
465
  referenceLocale,
448
466
  isBuiltIn,
@@ -460,14 +478,12 @@ class LocalizationAITranslateTask extends import_plugin_async_task_manager.TaskT
460
478
  sourceTextLength: (sourceText == null ? void 0 : sourceText.length) ?? 0,
461
479
  translationLength: translation.length,
462
480
  locale,
463
- sourceLang,
464
481
  targetLang,
465
482
  module: module2,
466
483
  employeeUsername,
467
484
  provider: service == null ? void 0 : service.provider,
468
485
  llmService: service == null ? void 0 : service.name,
469
486
  model,
470
- hasEnglishReference: Boolean(englishReference),
471
487
  hasReferenceTranslation: Boolean(referenceTranslation),
472
488
  referenceLocale,
473
489
  isBuiltIn,
@@ -477,32 +493,25 @@ class LocalizationAITranslateTask extends import_plugin_async_task_manager.TaskT
477
493
  });
478
494
  return translation;
479
495
  }
496
+ getEmployeeSystemPrompt(employee) {
497
+ var _a, _b;
498
+ return ((_a = employee == null ? void 0 : employee.get) == null ? void 0 : _a.call(employee, "about")) || ((_b = employee == null ? void 0 : employee.get) == null ? void 0 : _b.call(employee, "defaultPrompt")) || (employee == null ? void 0 : employee.about) || (employee == null ? void 0 : employee.defaultPrompt) || "";
499
+ }
480
500
  buildProviderContext(options) {
481
- const { sourceText, targetLang, referenceSourceTerm, referenceTargetTerm } = options;
482
- const reference = referenceSourceTerm && referenceTargetTerm ? `Refer to the following translation:
483
- ${referenceSourceTerm} is translated as ${referenceTargetTerm}
501
+ const { systemPrompt, sourceText, targetLang, referenceTranslation } = options;
502
+ const prompt = (systemPrompt || "").trim();
503
+ const reference = referenceTranslation ? `Refer to the following translation:
504
+ ${sourceText} is translated as ${referenceTranslation}
484
505
 
485
506
  ` : "";
486
- const content = `${reference}Translate the following text into ${targetLang}. Output only the translated result without any additional explanation:
487
-
507
+ const task = `Translate the following text into ${targetLang}. Output only the translated result without any additional explanation:
488
508
  ${sourceText}
489
509
  `;
510
+ const content = [prompt, `${reference}${task}`].filter(Boolean).join("\n\n");
490
511
  return {
491
512
  messages: [new import_messages.HumanMessage(content)]
492
513
  };
493
514
  }
494
- buildTranslationTerms(options) {
495
- const { sourceTerm, targetTerm, targetLang } = options;
496
- if (!sourceTerm || !targetTerm || !["Chinese", "Traditional Chinese"].includes(targetLang)) {
497
- return void 0;
498
- }
499
- return [
500
- {
501
- source: sourceTerm,
502
- target: targetTerm
503
- }
504
- ];
505
- }
506
515
  getLanguageName(locale) {
507
516
  const normalized = locale.replace("_", "-");
508
517
  const map = {
@@ -592,5 +601,6 @@ ${sourceText}
592
601
  // Annotate the CommonJS export names for ESM import in node:
593
602
  0 && (module.exports = {
594
603
  LOCALIZATION_AI_TRANSLATE_TASK_TYPE,
595
- LocalizationAITranslateTask
604
+ LocalizationAITranslateTask,
605
+ pickBuiltInResourceReference
596
606
  });