@procore/ai-translations 0.4.0 → 0.6.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.
@@ -52,6 +52,9 @@ var Config = class _Config {
52
52
  setToolName(name) {
53
53
  this.toolName = name;
54
54
  }
55
+ isBackendTranslationStrategy() {
56
+ return this.ToolConfig.strategy === "backend_translations";
57
+ }
55
58
  getToolName() {
56
59
  return this.toolName;
57
60
  }
@@ -245,10 +248,10 @@ var Hash = class {
245
248
  var QueueManager = class {
246
249
  constructor() {
247
250
  if (!globalThis.__AI_TRANSLATION_FRONTEND_QUEUE__) {
248
- globalThis.__AI_TRANSLATION_FRONTEND_QUEUE__ = /* @__PURE__ */ new Set();
251
+ globalThis.__AI_TRANSLATION_FRONTEND_QUEUE__ = /* @__PURE__ */ new Map();
249
252
  }
250
253
  if (!globalThis.__AI_TRANSLATION_BACKEND_QUEUE__) {
251
- globalThis.__AI_TRANSLATION_BACKEND_QUEUE__ = /* @__PURE__ */ new Set();
254
+ globalThis.__AI_TRANSLATION_BACKEND_QUEUE__ = /* @__PURE__ */ new Map();
252
255
  }
253
256
  }
254
257
  getQueue(strategy) {
@@ -261,42 +264,20 @@ var QueueManager = class {
261
264
  return void 0;
262
265
  }
263
266
  add(entry) {
264
- var _a;
265
- (_a = this.getQueue(entry.translationStrategy)) == null ? void 0 : _a.add(entry);
266
- }
267
- *generateBatches(strategy, config2) {
268
- const queue = this.getQueue(strategy);
269
- if (!queue) {
270
- throw new Error("Invalid translation strategy");
271
- }
272
- if (queue.size === 0) {
273
- console.warn("No translations to process");
274
- return;
275
- }
276
- const batch = config2.getToolConfig().translationBatchSize;
277
- let batches = [];
278
- for (const entry of queue.values()) {
279
- batches.push(entry);
280
- if (batches.length >= batch) {
281
- yield batches;
282
- batches = [];
283
- }
284
- }
285
- if (batches.length > 0) {
286
- queue.clear();
287
- yield batches;
288
- }
267
+ const queue = this.getQueue(entry.translationStrategy);
268
+ if (!queue || queue.has(entry.id)) return;
269
+ queue.set(entry.id, entry);
289
270
  }
290
- *generateBatchesV2(config2) {
271
+ *generateBatches(config2) {
291
272
  const queue = this.getQueue(config2.getToolConfig().strategy);
292
273
  if (!queue) throw new Error("Invalid translation strategy");
293
274
  const batchSize = config2.getToolConfig().translationBatchSize;
294
275
  while (queue.size > 0) {
295
276
  const batch = [];
296
277
  while (batch.length < batchSize && queue.size > 0) {
297
- const next = queue.values().next().value;
298
- queue.delete(next);
299
- batch.push(next);
278
+ const [key, entry] = queue.entries().next().value;
279
+ queue.delete(key);
280
+ batch.push(entry);
300
281
  }
301
282
  yield batch;
302
283
  }
@@ -415,6 +396,19 @@ var _Storage = class _Storage {
415
396
  }
416
397
  }
417
398
  // ------------------------------------
399
+ // 🗑️ Clear Translation by ID
400
+ // ------------------------------------
401
+ static async deleteById(id) {
402
+ try {
403
+ const db = await _Storage.getDB();
404
+ await db.delete(_Storage.STORE_NAME, id);
405
+ return true;
406
+ } catch (error) {
407
+ console.error("[Storage] Failed to delete by id:", error);
408
+ return false;
409
+ }
410
+ }
411
+ // ------------------------------------
418
412
  // 🗑️ Clear All Translations
419
413
  // ------------------------------------
420
414
  static async clearAll() {
@@ -489,10 +483,42 @@ var _TranslationRegistry = class _TranslationRegistry {
489
483
  isNonRetryable(key) {
490
484
  return _TranslationRegistry.nonRetryableTexts.has(key);
491
485
  }
492
- async get(text, targetLanguage, tool) {
486
+ enqueue(enqueuedKey) {
487
+ _TranslationRegistry.enqueuedItems.add(enqueuedKey);
488
+ }
489
+ isEnqueued(enqueuedKey) {
490
+ return _TranslationRegistry.enqueuedItems.has(enqueuedKey);
491
+ }
492
+ clearEnqueued() {
493
+ _TranslationRegistry.enqueuedItems.clear();
494
+ }
495
+ async removeTextsFromEnqueued(texts, targetLanguage, strategy) {
496
+ for (const text of texts) {
497
+ const id = await Storage.generateId(text, targetLanguage, this.tool);
498
+ _TranslationRegistry.enqueuedItems.delete(
499
+ this.generateEnqueuedKey(id, strategy)
500
+ );
501
+ }
502
+ }
503
+ static removeFromEnqueued(key) {
504
+ _TranslationRegistry.enqueuedItems.delete(key);
505
+ }
506
+ generateEnqueuedKey(id, strategy) {
507
+ return `${id}:${strategy}`;
508
+ }
509
+ async get(text, targetLanguage, tool, config2) {
493
510
  const key = await Hash.generateFromMultiple([text, targetLanguage, tool]);
494
- if (_TranslationRegistry.memoryCache.has(key)) {
495
- return _TranslationRegistry.memoryCache.get(key);
511
+ const cached = _TranslationRegistry.memoryCache.get(key);
512
+ if (cached) {
513
+ if (cached.translationStrategy === "frontend_translations" && config2.isBackendTranslationStrategy()) {
514
+ _TranslationRegistry.memoryCache.delete(key);
515
+ if (cached.translatedText) {
516
+ _TranslationRegistry.originalTextIndex.delete(cached.translatedText);
517
+ }
518
+ await Storage.deleteById(key);
519
+ return void 0;
520
+ }
521
+ return cached;
496
522
  }
497
523
  const translation = await Storage.getTranslation(
498
524
  text,
@@ -500,6 +526,10 @@ var _TranslationRegistry = class _TranslationRegistry {
500
526
  tool
501
527
  );
502
528
  if (translation !== void 0) {
529
+ if (translation.translation_strategy === "frontend_translations" && config2.isBackendTranslationStrategy()) {
530
+ await Storage.deleteById(translation.id);
531
+ return void 0;
532
+ }
503
533
  return this.toTranslationRegistryEntry(translation);
504
534
  }
505
535
  return void 0;
@@ -514,6 +544,7 @@ var _TranslationRegistry = class _TranslationRegistry {
514
544
  async clear() {
515
545
  _TranslationRegistry.memoryCache.clear();
516
546
  _TranslationRegistry.originalTextIndex.clear();
547
+ _TranslationRegistry.enqueuedItems.clear();
517
548
  await Storage.clearAll();
518
549
  return;
519
550
  }
@@ -521,6 +552,7 @@ var _TranslationRegistry = class _TranslationRegistry {
521
552
  await Storage.deleteByToolAndStrategy(tool, strategy);
522
553
  _TranslationRegistry.memoryCache.clear();
523
554
  _TranslationRegistry.originalTextIndex.clear();
555
+ _TranslationRegistry.enqueuedItems.clear();
524
556
  return;
525
557
  }
526
558
  toTranslationRegistryEntry(translation) {
@@ -542,10 +574,16 @@ var _TranslationRegistry = class _TranslationRegistry {
542
574
  translation.id
543
575
  );
544
576
  const newTranslation = this.toTranslationRegistryEntry(translation);
577
+ const enqueuedKey = this.generateEnqueuedKey(
578
+ translation.id,
579
+ translation.translation_strategy
580
+ );
545
581
  if (currentTranslation && !this.shouldUpdateTranslation(currentTranslation, newTranslation)) {
582
+ _TranslationRegistry.removeFromEnqueued(enqueuedKey);
546
583
  continue;
547
584
  }
548
585
  this.setTranslationToRegistry(newTranslation);
586
+ _TranslationRegistry.removeFromEnqueued(enqueuedKey);
549
587
  }
550
588
  return;
551
589
  }
@@ -588,6 +626,7 @@ var _TranslationRegistry = class _TranslationRegistry {
588
626
  __publicField(_TranslationRegistry, "memoryCache", /* @__PURE__ */ new Map());
589
627
  __publicField(_TranslationRegistry, "originalTextIndex", /* @__PURE__ */ new Map());
590
628
  __publicField(_TranslationRegistry, "nonRetryableTexts", /* @__PURE__ */ new Set());
629
+ __publicField(_TranslationRegistry, "enqueuedItems", /* @__PURE__ */ new Set());
591
630
  var TranslationRegistry = _TranslationRegistry;
592
631
 
593
632
  // src/utils/eventHandler.ts
@@ -608,12 +647,13 @@ var EventHandler = class {
608
647
  this.aiTranslationEvents = new SystemEvents(this.sourceEventIdentifier);
609
648
  this.toolName = toolName;
610
649
  }
611
- publishTranslationCompleteEvent(sourceTranslatedTexts, nonRetryableTexts) {
650
+ publishTranslationCompleteEvent(sourceTranslatedTexts, nonRetryableTexts, retryableFailedTexts = []) {
612
651
  this.aiTranslationEvents.publish(
613
652
  this.withEventName(TRANSLATION_COMPLETE_EVENT_NAME),
614
653
  {
615
654
  sourceTranslatedTexts,
616
- nonRetryableTexts
655
+ nonRetryableTexts,
656
+ retryableFailedTexts
617
657
  }
618
658
  );
619
659
  }
@@ -621,12 +661,19 @@ var EventHandler = class {
621
661
  return this.aiTranslationEvents.subscribe(
622
662
  this.withEventName(TRANSLATION_COMPLETE_EVENT_NAME),
623
663
  (detail) => {
624
- var _a, _b;
664
+ var _a, _b, _c;
625
665
  const sourceTranslatedTexts = Array.isArray(
626
666
  (_a = detail.data) == null ? void 0 : _a.sourceTranslatedTexts
627
667
  ) ? detail.data.sourceTranslatedTexts : [];
628
668
  const nonRetryableTexts = Array.isArray((_b = detail.data) == null ? void 0 : _b.nonRetryableTexts) ? detail.data.nonRetryableTexts : [];
629
- callback(sourceTranslatedTexts, nonRetryableTexts);
669
+ const retryableFailedTexts = Array.isArray(
670
+ (_c = detail.data) == null ? void 0 : _c.retryableFailedTexts
671
+ ) ? detail.data.retryableFailedTexts : [];
672
+ callback(
673
+ sourceTranslatedTexts,
674
+ nonRetryableTexts,
675
+ retryableFailedTexts
676
+ );
630
677
  }
631
678
  );
632
679
  }
@@ -727,6 +774,7 @@ var TranslationManager = class {
727
774
  __publicField(this, "currentTranslatorStrategy");
728
775
  __publicField(this, "sourceTexts", /* @__PURE__ */ new Map());
729
776
  __publicField(this, "nonRetryableTexts", /* @__PURE__ */ new Set());
777
+ __publicField(this, "retryableFailedTexts", /* @__PURE__ */ new Map());
730
778
  __publicField(this, "progressEventHandler");
731
779
  __publicField(this, "translationProgress", {
732
780
  progress: 0,
@@ -754,19 +802,21 @@ var TranslationManager = class {
754
802
  this.translationProgress.total = queueManager.queueSize(
755
803
  this.currentTranslatorStrategy
756
804
  );
757
- for (const batch of queueManager.generateBatchesV2(
758
- this.translator.getConfig()
759
- )) {
760
- batch.forEach((entry) => this.mfeToBeNotified.add(entry.tool));
761
- const result = await this.translator.processTranslations(
762
- batch.map(
763
- (entry) => this.convertTranslationQueueEntryToTranslationRequest(entry)
764
- )
765
- );
766
- this.setTranslationProgress(batch.length);
767
- await this.updateDatabaseWithTranslations(result);
768
- await this.notifyTranslationCompleted();
769
- }
805
+ do {
806
+ for (const batch of queueManager.generateBatches(
807
+ this.translator.getConfig()
808
+ )) {
809
+ batch.forEach((entry) => this.mfeToBeNotified.add(entry.tool));
810
+ const result = await this.translator.processTranslations(
811
+ batch.map(
812
+ (entry) => this.convertTranslationQueueEntryToTranslationRequest(entry)
813
+ )
814
+ );
815
+ this.setTranslationProgress(batch.length);
816
+ await this.updateDatabaseWithTranslations(result);
817
+ await this.notifyTranslationCompleted();
818
+ }
819
+ } while (queueManager.queueSize(this.currentTranslatorStrategy) > 0);
770
820
  this.resetTranslationProgress();
771
821
  this.publishTranslationProgress();
772
822
  if (this.currentTranslatorStrategy === "frontend_translations") {
@@ -785,6 +835,7 @@ var TranslationManager = class {
785
835
  continue;
786
836
  }
787
837
  if (!translation.isTranslated) {
838
+ this.addRetryableFailedText(response.tool, translation.sourceText);
788
839
  continue;
789
840
  }
790
841
  this.addSourceTextToMap(response.tool, translation.sourceText);
@@ -804,12 +855,14 @@ var TranslationManager = class {
804
855
  const eventhandler = new EventHandler(mfe);
805
856
  eventhandler.publishTranslationCompleteEvent(
806
857
  Array.from(this.sourceTexts.get(mfe) ?? /* @__PURE__ */ new Set()),
807
- Array.from(this.nonRetryableTexts)
858
+ Array.from(this.nonRetryableTexts),
859
+ Array.from(this.retryableFailedTexts.get(mfe) ?? /* @__PURE__ */ new Set())
808
860
  );
809
861
  }
810
862
  this.mfeToBeNotified.clear();
811
863
  this.sourceTexts.clear();
812
864
  this.nonRetryableTexts.clear();
865
+ this.retryableFailedTexts.clear();
813
866
  }
814
867
  setTranslationProgress(batchSize) {
815
868
  this.translationProgress.current += batchSize;
@@ -846,6 +899,11 @@ var TranslationManager = class {
846
899
  existing.add(sourceText);
847
900
  this.sourceTexts.set(tool, existing);
848
901
  }
902
+ addRetryableFailedText(tool, sourceText) {
903
+ const existing = this.retryableFailedTexts.get(tool) ?? /* @__PURE__ */ new Set();
904
+ existing.add(sourceText);
905
+ this.retryableFailedTexts.set(tool, existing);
906
+ }
849
907
  publishTranslationProgress() {
850
908
  this.progressEventHandler.publishTranslationProgressEvent(
851
909
  this.translationProgress.progress,
@@ -912,8 +970,16 @@ var Client = class {
912
970
  method: "POST",
913
971
  body: JSON.stringify(requests)
914
972
  });
973
+ const responseBody = Array.isArray(data) ? data.map((group) => ({
974
+ ...group,
975
+ translations: Array.isArray(group.translations) ? group.translations.map((t) => ({
976
+ ...t,
977
+ isTranslated: t.isTranslated ?? (!!t.translation && t.translation !== ""),
978
+ retryable: t.retryable ?? true
979
+ })) : []
980
+ })) : data;
915
981
  return {
916
- responseBody: data,
982
+ responseBody,
917
983
  success: true
918
984
  };
919
985
  } catch (error) {
@@ -991,11 +1057,54 @@ var getModelDownloadEventHandler = () => {
991
1057
  }
992
1058
  return _modelDownloadEventHandler;
993
1059
  };
1060
+ var SUPPORTED_LANGUAGES = /* @__PURE__ */ new Set([
1061
+ "ar",
1062
+ "bn",
1063
+ "bg",
1064
+ "zh",
1065
+ "hr",
1066
+ "cs",
1067
+ "da",
1068
+ "nl",
1069
+ "en",
1070
+ "fi",
1071
+ "fr",
1072
+ "de",
1073
+ "el",
1074
+ "he",
1075
+ "hi",
1076
+ "hu",
1077
+ "id",
1078
+ "it",
1079
+ "ja",
1080
+ "kn",
1081
+ "ko",
1082
+ "lt",
1083
+ "mr",
1084
+ "no",
1085
+ "pl",
1086
+ "pt",
1087
+ "ro",
1088
+ "ru",
1089
+ "sk",
1090
+ "sl",
1091
+ "es",
1092
+ "sv",
1093
+ "ta",
1094
+ "te",
1095
+ "th",
1096
+ "tr",
1097
+ "uk",
1098
+ "vi"
1099
+ ]);
994
1100
  var _ChromeTranslator = class _ChromeTranslator {
995
1101
  constructor(translator) {
996
1102
  __publicField(this, "translator");
997
1103
  this.translator = translator;
998
1104
  }
1105
+ static isSupportedLanguage(lang) {
1106
+ return SUPPORTED_LANGUAGES.has(lang) || SUPPORTED_LANGUAGES.has(lang.split("-")[0] ?? "");
1107
+ }
999
1108
  static ensureRegistry() {
1000
1109
  if (!globalThis.chromeTranslatorRegistry) {
1001
1110
  globalThis.chromeTranslatorRegistry = /* @__PURE__ */ new Map();
@@ -1017,30 +1126,31 @@ var _ChromeTranslator = class _ChromeTranslator {
1017
1126
  );
1018
1127
  }
1019
1128
  static async createTranslator(source, target) {
1129
+ const key = this.generateKey(source, target);
1020
1130
  let resolveReady;
1021
- this.translatorReadyPromise = new Promise((resolve) => {
1131
+ const readyPromise = new Promise((resolve) => {
1022
1132
  resolveReady = resolve;
1023
1133
  });
1134
+ this.translatorReadyPromises.set(key, readyPromise);
1024
1135
  let isDownloading = false;
1025
1136
  const translator = await Translator.create({
1026
1137
  sourceLanguage: source,
1027
1138
  targetLanguage: target,
1028
1139
  monitor(m) {
1029
- m.addEventListener(
1030
- "downloadprogress",
1031
- (e) => {
1032
- isDownloading = true;
1033
- const progress = e.total > 0 ? Math.round(e.loaded / e.total * 100) : 0;
1034
- getModelDownloadEventHandler().publishModelDownloadProgressEvent(
1035
- e.loaded,
1036
- e.total,
1037
- progress
1038
- );
1039
- if (e.loaded === e.total) {
1040
- resolveReady();
1041
- }
1140
+ m.addEventListener("downloadprogress", (e) => {
1141
+ isDownloading = true;
1142
+ const loaded = e.loaded ?? 0;
1143
+ const total = e.total ?? 0;
1144
+ const progress = total > 0 ? Math.round(loaded / total * 100) : 0;
1145
+ getModelDownloadEventHandler().publishModelDownloadProgressEvent(
1146
+ loaded,
1147
+ total,
1148
+ progress
1149
+ );
1150
+ if (total > 0 && loaded >= total) {
1151
+ resolveReady();
1042
1152
  }
1043
- );
1153
+ });
1044
1154
  }
1045
1155
  });
1046
1156
  if (!isDownloading) {
@@ -1048,8 +1158,8 @@ var _ChromeTranslator = class _ChromeTranslator {
1048
1158
  }
1049
1159
  return translator;
1050
1160
  }
1051
- static waitForReady() {
1052
- return this.translatorReadyPromise;
1161
+ static waitForReady(key) {
1162
+ return this.translatorReadyPromises.get(key) ?? Promise.resolve();
1053
1163
  }
1054
1164
  static async translate(text, targetLanguage) {
1055
1165
  var _a;
@@ -1058,8 +1168,17 @@ var _ChromeTranslator = class _ChromeTranslator {
1058
1168
  const detector = await ChromeLanguageDetector.getInstance();
1059
1169
  const sourceLanguage = await detector.detectLanguage(text);
1060
1170
  const key = this.generateKey(sourceLanguage, targetLanguage);
1061
- let instance = globalThis.chromeTranslatorRegistry.get(key);
1062
- if (!instance) {
1171
+ if (!this.isSupportedLanguage(sourceLanguage) || !this.isSupportedLanguage(targetLanguage)) {
1172
+ return {
1173
+ translation: text,
1174
+ sourceLanguage,
1175
+ success: false,
1176
+ retryable: false,
1177
+ errorMessage: `Unsupported language pair: ${sourceLanguage} \u2192 ${targetLanguage}`
1178
+ };
1179
+ }
1180
+ const existingInstance = globalThis.chromeTranslatorRegistry.get(key);
1181
+ if (!existingInstance) {
1063
1182
  const translatorCapabilities = await Translator.availability({
1064
1183
  sourceLanguage,
1065
1184
  targetLanguage
@@ -1077,10 +1196,16 @@ var _ChromeTranslator = class _ChromeTranslator {
1077
1196
  sourceLanguage,
1078
1197
  targetLanguage
1079
1198
  );
1080
- instance = new _ChromeTranslator(chromeTranslator);
1081
- globalThis.chromeTranslatorRegistry.set(key, instance);
1199
+ if (!globalThis.chromeTranslatorRegistry.has(key)) {
1200
+ globalThis.chromeTranslatorRegistry.set(
1201
+ key,
1202
+ new _ChromeTranslator(chromeTranslator)
1203
+ );
1204
+ }
1082
1205
  }
1083
- await this.waitForReady();
1206
+ const instance = globalThis.chromeTranslatorRegistry.get(key);
1207
+ await this.waitForReady(key);
1208
+ this.translatorReadyPromises.delete(key);
1084
1209
  const translation = await ((_a = instance.translator) == null ? void 0 : _a.translate(text));
1085
1210
  if (!translation) {
1086
1211
  return {
@@ -1088,7 +1213,7 @@ var _ChromeTranslator = class _ChromeTranslator {
1088
1213
  sourceLanguage,
1089
1214
  success: false,
1090
1215
  errorMessage: "Translation failed",
1091
- retryable: true
1216
+ retryable: false
1092
1217
  };
1093
1218
  }
1094
1219
  return {
@@ -1098,11 +1223,12 @@ var _ChromeTranslator = class _ChromeTranslator {
1098
1223
  retryable: true
1099
1224
  };
1100
1225
  } catch (error) {
1226
+ const message = error instanceof Error ? error.message : "Unknown error";
1101
1227
  return {
1102
1228
  translation: text,
1103
1229
  sourceLanguage: targetLanguage,
1104
1230
  success: false,
1105
- errorMessage: error instanceof Error ? error.message : "Unknown error",
1231
+ errorMessage: message,
1106
1232
  retryable: true
1107
1233
  };
1108
1234
  }
@@ -1111,7 +1237,7 @@ var _ChromeTranslator = class _ChromeTranslator {
1111
1237
  return `${sourceLanguage.toUpperCase()}-${targetLanguage.toUpperCase()}`;
1112
1238
  }
1113
1239
  };
1114
- __publicField(_ChromeTranslator, "translatorReadyPromise", Promise.resolve());
1240
+ __publicField(_ChromeTranslator, "translatorReadyPromises", /* @__PURE__ */ new Map());
1115
1241
  var ChromeTranslator = _ChromeTranslator;
1116
1242
 
1117
1243
  // src/translators/frontend_translators/chrome/client.ts
@@ -1261,13 +1387,22 @@ var aitFunction = async (text, translationRegistry, config2, tool) => {
1261
1387
  const existingByOriginal = await translationRegistry.get(
1262
1388
  text,
1263
1389
  targetLanguage,
1264
- tool
1390
+ tool,
1391
+ config2
1265
1392
  );
1266
1393
  if (existingByOriginal && existingByOriginal.isTranslated && existingByOriginal.translatedText && existingByOriginal.translatedText.trim() !== "") {
1267
1394
  return existingByOriginal.translatedText;
1268
1395
  }
1269
1396
  if (!existingByOriginal) {
1397
+ const id = await Storage.generateId(text, targetLanguage, tool);
1398
+ const strategy = config2.getToolConfig().strategy;
1399
+ const enqueuedKey = translationRegistry.generateEnqueuedKey(id, strategy);
1400
+ if (translationRegistry.isEnqueued(enqueuedKey)) {
1401
+ return text;
1402
+ }
1403
+ translationRegistry.enqueue(enqueuedKey);
1270
1404
  queueManager.add({
1405
+ id,
1271
1406
  originalText: text,
1272
1407
  targetLanguage,
1273
1408
  tool,
@@ -1331,12 +1466,17 @@ function AITranslationInnerProvider(props) {
1331
1466
  });
1332
1467
  useEffect(() => {
1333
1468
  const unsubscribe = eventHandler.current.subscribeToTranslationCompleteEvent(
1334
- async (sourceTranslatedTexts, nonRetryableTexts) => {
1469
+ async (sourceTranslatedTexts, nonRetryableTexts, retryableFailedTexts) => {
1335
1470
  try {
1336
1471
  await translationRegistry.current.populateRegistryFromDatabase();
1337
1472
  translationRegistry.current.populateNonRetryableTexts(
1338
1473
  nonRetryableTexts
1339
1474
  );
1475
+ translationRegistry.current.removeTextsFromEnqueued(
1476
+ retryableFailedTexts,
1477
+ locale,
1478
+ config2.getToolConfig().strategy
1479
+ );
1340
1480
  eventHandler.current.publishRerenderEvent(sourceTranslatedTexts);
1341
1481
  } catch (error) {
1342
1482
  console.error(
@@ -1347,7 +1487,7 @@ function AITranslationInnerProvider(props) {
1347
1487
  }
1348
1488
  );
1349
1489
  return () => unsubscribe();
1350
- }, []);
1490
+ }, [locale, config2]);
1351
1491
  useEffect(() => {
1352
1492
  const unsubscribe = eventHandler.current.subscribeToTranslationProgressEvent(
1353
1493
  (progress, current, total) => {
@@ -1365,15 +1505,15 @@ function AITranslationInnerProvider(props) {
1365
1505
  return () => unsubscribe();
1366
1506
  }, []);
1367
1507
  useEffect(() => {
1368
- if (isFetched && remoteConfig && remoteConfig[tool]) {
1369
- setConfig((prevConfig) => {
1370
- const clonedConfig = prevConfig.clone();
1371
- clonedConfig.setTargetLanguage(locale);
1372
- clonedConfig.setToolConfig(remoteConfig[tool]);
1373
- translator.current.updateConfig(clonedConfig);
1374
- return clonedConfig;
1375
- });
1376
- }
1508
+ if (!isFetched || !remoteConfig) return;
1509
+ const toolConfig = remoteConfig[tool] ?? remoteConfig;
1510
+ setConfig((prevConfig) => {
1511
+ const clonedConfig = prevConfig.clone();
1512
+ clonedConfig.setTargetLanguage(locale);
1513
+ clonedConfig.setToolConfig(toolConfig);
1514
+ translator.current.updateConfig(clonedConfig);
1515
+ return clonedConfig;
1516
+ });
1377
1517
  }, [tool, remoteConfig, isFetched, locale]);
1378
1518
  config2.setTargetLanguage(locale);
1379
1519
  config2.setToolName(tool);
@@ -1436,6 +1576,7 @@ var TranslatedIcon = ({
1436
1576
  className,
1437
1577
  xmlns: "http://www.w3.org/2000/svg",
1438
1578
  "aria-hidden": "true",
1579
+ style: { marginRight: "10px", flexShrink: 0 },
1439
1580
  children: /* @__PURE__ */ jsx2(
1440
1581
  "path",
1441
1582
  {
@@ -1470,12 +1611,14 @@ var AITranslateText = ({
1470
1611
  return;
1471
1612
  }
1472
1613
  if (sourceTexts.includes(text)) {
1473
- setDisplayText(await context.ait(text));
1614
+ const translatedText = await context.ait(text);
1615
+ setDisplayText(translatedText);
1616
+ setShowHighlightState(showHighlight && translatedText !== text);
1474
1617
  }
1475
1618
  }
1476
1619
  );
1477
1620
  return () => unsubscribe();
1478
- }, [context, text]);
1621
+ }, [context, text, showHighlight]);
1479
1622
  const reset = useCallback2(
1480
1623
  (displayValue = text) => {
1481
1624
  setDisplayText(displayValue);
@@ -1498,7 +1641,7 @@ var AITranslateText = ({
1498
1641
  const translatedText = await context.ait(text);
1499
1642
  if (cancelled) return;
1500
1643
  setDisplayText(translatedText);
1501
- setShowHighlightState(showHighlight);
1644
+ setShowHighlightState(showHighlight && translatedText !== text);
1502
1645
  } catch {
1503
1646
  if (cancelled) return;
1504
1647
  reset();
@@ -1510,8 +1653,8 @@ var AITranslateText = ({
1510
1653
  };
1511
1654
  }, [text, shouldTranslate, context, showHighlight, reset]);
1512
1655
  return /* @__PURE__ */ jsxs(Fragment, { children: [
1513
- displayText,
1514
- showHighlightState && /* @__PURE__ */ jsx3(TranslatedIcon, { ...translatedIconProps })
1656
+ showHighlightState && /* @__PURE__ */ jsx3(TranslatedIcon, { ...translatedIconProps }),
1657
+ displayText
1515
1658
  ] });
1516
1659
  };
1517
1660
 
@@ -17,6 +17,7 @@ interface TranslationProgress {
17
17
  total: number;
18
18
  }
19
19
  interface TranslationQueueEntry {
20
+ id: string;
20
21
  originalText: string;
21
22
  isTranslated: boolean;
22
23
  targetLanguage: string;
@@ -170,8 +171,8 @@ declare const AI_TRANSLATION_FEATURE_FLAG_KEY = "ai-translation";
170
171
  declare const getAITranslationLDId: (domain: string) => "67e17b925ace3c08088c4bd2" | "570f2f6e348a2806d7006b6f" | "570e9423348a2806d7004354" | "570f37b4eee8b907140070c5";
171
172
 
172
173
  declare global {
173
- var __AI_TRANSLATION_FRONTEND_QUEUE__: Set<TranslationQueueEntry> | undefined;
174
- var __AI_TRANSLATION_BACKEND_QUEUE__: Set<TranslationQueueEntry> | undefined;
174
+ var __AI_TRANSLATION_FRONTEND_QUEUE__: Map<string, TranslationQueueEntry> | undefined;
175
+ var __AI_TRANSLATION_BACKEND_QUEUE__: Map<string, TranslationQueueEntry> | undefined;
175
176
  var _FRONTEND_AI_TRANSLATION_IN_PROGRESS_: boolean;
176
177
  var _BACKEND_AI_TRANSLATION_IN_PROGRESS_: boolean;
177
178
  }
@@ -17,6 +17,7 @@ interface TranslationProgress {
17
17
  total: number;
18
18
  }
19
19
  interface TranslationQueueEntry {
20
+ id: string;
20
21
  originalText: string;
21
22
  isTranslated: boolean;
22
23
  targetLanguage: string;
@@ -170,8 +171,8 @@ declare const AI_TRANSLATION_FEATURE_FLAG_KEY = "ai-translation";
170
171
  declare const getAITranslationLDId: (domain: string) => "67e17b925ace3c08088c4bd2" | "570f2f6e348a2806d7006b6f" | "570e9423348a2806d7004354" | "570f37b4eee8b907140070c5";
171
172
 
172
173
  declare global {
173
- var __AI_TRANSLATION_FRONTEND_QUEUE__: Set<TranslationQueueEntry> | undefined;
174
- var __AI_TRANSLATION_BACKEND_QUEUE__: Set<TranslationQueueEntry> | undefined;
174
+ var __AI_TRANSLATION_FRONTEND_QUEUE__: Map<string, TranslationQueueEntry> | undefined;
175
+ var __AI_TRANSLATION_BACKEND_QUEUE__: Map<string, TranslationQueueEntry> | undefined;
175
176
  var _FRONTEND_AI_TRANSLATION_IN_PROGRESS_: boolean;
176
177
  var _BACKEND_AI_TRANSLATION_IN_PROGRESS_: boolean;
177
178
  }