@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.
@@ -77,6 +77,9 @@ var Config = class _Config {
77
77
  setToolName(name) {
78
78
  this.toolName = name;
79
79
  }
80
+ isBackendTranslationStrategy() {
81
+ return this.ToolConfig.strategy === "backend_translations";
82
+ }
80
83
  getToolName() {
81
84
  return this.toolName;
82
85
  }
@@ -264,10 +267,10 @@ var Hash = class {
264
267
  var QueueManager = class {
265
268
  constructor() {
266
269
  if (!globalThis.__AI_TRANSLATION_FRONTEND_QUEUE__) {
267
- globalThis.__AI_TRANSLATION_FRONTEND_QUEUE__ = /* @__PURE__ */ new Set();
270
+ globalThis.__AI_TRANSLATION_FRONTEND_QUEUE__ = /* @__PURE__ */ new Map();
268
271
  }
269
272
  if (!globalThis.__AI_TRANSLATION_BACKEND_QUEUE__) {
270
- globalThis.__AI_TRANSLATION_BACKEND_QUEUE__ = /* @__PURE__ */ new Set();
273
+ globalThis.__AI_TRANSLATION_BACKEND_QUEUE__ = /* @__PURE__ */ new Map();
271
274
  }
272
275
  }
273
276
  getQueue(strategy) {
@@ -280,41 +283,20 @@ var QueueManager = class {
280
283
  return void 0;
281
284
  }
282
285
  add(entry) {
283
- this.getQueue(entry.translationStrategy)?.add(entry);
284
- }
285
- *generateBatches(strategy, config2) {
286
- const queue = this.getQueue(strategy);
287
- if (!queue) {
288
- throw new Error("Invalid translation strategy");
289
- }
290
- if (queue.size === 0) {
291
- console.warn("No translations to process");
292
- return;
293
- }
294
- const batch = config2.getToolConfig().translationBatchSize;
295
- let batches = [];
296
- for (const entry of queue.values()) {
297
- batches.push(entry);
298
- if (batches.length >= batch) {
299
- yield batches;
300
- batches = [];
301
- }
302
- }
303
- if (batches.length > 0) {
304
- queue.clear();
305
- yield batches;
306
- }
286
+ const queue = this.getQueue(entry.translationStrategy);
287
+ if (!queue || queue.has(entry.id)) return;
288
+ queue.set(entry.id, entry);
307
289
  }
308
- *generateBatchesV2(config2) {
290
+ *generateBatches(config2) {
309
291
  const queue = this.getQueue(config2.getToolConfig().strategy);
310
292
  if (!queue) throw new Error("Invalid translation strategy");
311
293
  const batchSize = config2.getToolConfig().translationBatchSize;
312
294
  while (queue.size > 0) {
313
295
  const batch = [];
314
296
  while (batch.length < batchSize && queue.size > 0) {
315
- const next = queue.values().next().value;
316
- queue.delete(next);
317
- batch.push(next);
297
+ const [key, entry] = queue.entries().next().value;
298
+ queue.delete(key);
299
+ batch.push(entry);
318
300
  }
319
301
  yield batch;
320
302
  }
@@ -435,6 +417,19 @@ var Storage = class _Storage {
435
417
  }
436
418
  }
437
419
  // ------------------------------------
420
+ // 🗑️ Clear Translation by ID
421
+ // ------------------------------------
422
+ static async deleteById(id) {
423
+ try {
424
+ const db = await _Storage.getDB();
425
+ await db.delete(_Storage.STORE_NAME, id);
426
+ return true;
427
+ } catch (error) {
428
+ console.error("[Storage] Failed to delete by id:", error);
429
+ return false;
430
+ }
431
+ }
432
+ // ------------------------------------
438
433
  // 🗑️ Clear All Translations
439
434
  // ------------------------------------
440
435
  static async clearAll() {
@@ -463,6 +458,7 @@ var TranslationRegistry = class _TranslationRegistry {
463
458
  static memoryCache = /* @__PURE__ */ new Map();
464
459
  static originalTextIndex = /* @__PURE__ */ new Map();
465
460
  static nonRetryableTexts = /* @__PURE__ */ new Set();
461
+ static enqueuedItems = /* @__PURE__ */ new Set();
466
462
  tool;
467
463
  constructor(tool) {
468
464
  this.tool = tool;
@@ -516,10 +512,42 @@ var TranslationRegistry = class _TranslationRegistry {
516
512
  isNonRetryable(key) {
517
513
  return _TranslationRegistry.nonRetryableTexts.has(key);
518
514
  }
519
- async get(text, targetLanguage, tool) {
515
+ enqueue(enqueuedKey) {
516
+ _TranslationRegistry.enqueuedItems.add(enqueuedKey);
517
+ }
518
+ isEnqueued(enqueuedKey) {
519
+ return _TranslationRegistry.enqueuedItems.has(enqueuedKey);
520
+ }
521
+ clearEnqueued() {
522
+ _TranslationRegistry.enqueuedItems.clear();
523
+ }
524
+ async removeTextsFromEnqueued(texts, targetLanguage, strategy) {
525
+ for (const text of texts) {
526
+ const id = await Storage.generateId(text, targetLanguage, this.tool);
527
+ _TranslationRegistry.enqueuedItems.delete(
528
+ this.generateEnqueuedKey(id, strategy)
529
+ );
530
+ }
531
+ }
532
+ static removeFromEnqueued(key) {
533
+ _TranslationRegistry.enqueuedItems.delete(key);
534
+ }
535
+ generateEnqueuedKey(id, strategy) {
536
+ return `${id}:${strategy}`;
537
+ }
538
+ async get(text, targetLanguage, tool, config2) {
520
539
  const key = await Hash.generateFromMultiple([text, targetLanguage, tool]);
521
- if (_TranslationRegistry.memoryCache.has(key)) {
522
- return _TranslationRegistry.memoryCache.get(key);
540
+ const cached = _TranslationRegistry.memoryCache.get(key);
541
+ if (cached) {
542
+ if (cached.translationStrategy === "frontend_translations" && config2.isBackendTranslationStrategy()) {
543
+ _TranslationRegistry.memoryCache.delete(key);
544
+ if (cached.translatedText) {
545
+ _TranslationRegistry.originalTextIndex.delete(cached.translatedText);
546
+ }
547
+ await Storage.deleteById(key);
548
+ return void 0;
549
+ }
550
+ return cached;
523
551
  }
524
552
  const translation = await Storage.getTranslation(
525
553
  text,
@@ -527,6 +555,10 @@ var TranslationRegistry = class _TranslationRegistry {
527
555
  tool
528
556
  );
529
557
  if (translation !== void 0) {
558
+ if (translation.translation_strategy === "frontend_translations" && config2.isBackendTranslationStrategy()) {
559
+ await Storage.deleteById(translation.id);
560
+ return void 0;
561
+ }
530
562
  return this.toTranslationRegistryEntry(translation);
531
563
  }
532
564
  return void 0;
@@ -541,6 +573,7 @@ var TranslationRegistry = class _TranslationRegistry {
541
573
  async clear() {
542
574
  _TranslationRegistry.memoryCache.clear();
543
575
  _TranslationRegistry.originalTextIndex.clear();
576
+ _TranslationRegistry.enqueuedItems.clear();
544
577
  await Storage.clearAll();
545
578
  return;
546
579
  }
@@ -548,6 +581,7 @@ var TranslationRegistry = class _TranslationRegistry {
548
581
  await Storage.deleteByToolAndStrategy(tool, strategy);
549
582
  _TranslationRegistry.memoryCache.clear();
550
583
  _TranslationRegistry.originalTextIndex.clear();
584
+ _TranslationRegistry.enqueuedItems.clear();
551
585
  return;
552
586
  }
553
587
  toTranslationRegistryEntry(translation) {
@@ -569,10 +603,16 @@ var TranslationRegistry = class _TranslationRegistry {
569
603
  translation.id
570
604
  );
571
605
  const newTranslation = this.toTranslationRegistryEntry(translation);
606
+ const enqueuedKey = this.generateEnqueuedKey(
607
+ translation.id,
608
+ translation.translation_strategy
609
+ );
572
610
  if (currentTranslation && !this.shouldUpdateTranslation(currentTranslation, newTranslation)) {
611
+ _TranslationRegistry.removeFromEnqueued(enqueuedKey);
573
612
  continue;
574
613
  }
575
614
  this.setTranslationToRegistry(newTranslation);
615
+ _TranslationRegistry.removeFromEnqueued(enqueuedKey);
576
616
  }
577
617
  return;
578
618
  }
@@ -622,12 +662,13 @@ var EventHandler = class {
622
662
  this.aiTranslationEvents = new import_web_sdk_events.SystemEvents(this.sourceEventIdentifier);
623
663
  this.toolName = toolName;
624
664
  }
625
- publishTranslationCompleteEvent(sourceTranslatedTexts, nonRetryableTexts) {
665
+ publishTranslationCompleteEvent(sourceTranslatedTexts, nonRetryableTexts, retryableFailedTexts = []) {
626
666
  this.aiTranslationEvents.publish(
627
667
  this.withEventName(TRANSLATION_COMPLETE_EVENT_NAME),
628
668
  {
629
669
  sourceTranslatedTexts,
630
- nonRetryableTexts
670
+ nonRetryableTexts,
671
+ retryableFailedTexts
631
672
  }
632
673
  );
633
674
  }
@@ -639,7 +680,14 @@ var EventHandler = class {
639
680
  detail.data?.sourceTranslatedTexts
640
681
  ) ? detail.data.sourceTranslatedTexts : [];
641
682
  const nonRetryableTexts = Array.isArray(detail.data?.nonRetryableTexts) ? detail.data.nonRetryableTexts : [];
642
- callback(sourceTranslatedTexts, nonRetryableTexts);
683
+ const retryableFailedTexts = Array.isArray(
684
+ detail.data?.retryableFailedTexts
685
+ ) ? detail.data.retryableFailedTexts : [];
686
+ callback(
687
+ sourceTranslatedTexts,
688
+ nonRetryableTexts,
689
+ retryableFailedTexts
690
+ );
643
691
  }
644
692
  );
645
693
  }
@@ -728,6 +776,7 @@ var TranslationManager = class {
728
776
  currentTranslatorStrategy;
729
777
  sourceTexts = /* @__PURE__ */ new Map();
730
778
  nonRetryableTexts = /* @__PURE__ */ new Set();
779
+ retryableFailedTexts = /* @__PURE__ */ new Map();
731
780
  progressEventHandler;
732
781
  translationProgress = {
733
782
  progress: 0,
@@ -756,19 +805,21 @@ var TranslationManager = class {
756
805
  this.translationProgress.total = queueManager.queueSize(
757
806
  this.currentTranslatorStrategy
758
807
  );
759
- for (const batch of queueManager.generateBatchesV2(
760
- this.translator.getConfig()
761
- )) {
762
- batch.forEach((entry) => this.mfeToBeNotified.add(entry.tool));
763
- const result = await this.translator.processTranslations(
764
- batch.map(
765
- (entry) => this.convertTranslationQueueEntryToTranslationRequest(entry)
766
- )
767
- );
768
- this.setTranslationProgress(batch.length);
769
- await this.updateDatabaseWithTranslations(result);
770
- await this.notifyTranslationCompleted();
771
- }
808
+ do {
809
+ for (const batch of queueManager.generateBatches(
810
+ this.translator.getConfig()
811
+ )) {
812
+ batch.forEach((entry) => this.mfeToBeNotified.add(entry.tool));
813
+ const result = await this.translator.processTranslations(
814
+ batch.map(
815
+ (entry) => this.convertTranslationQueueEntryToTranslationRequest(entry)
816
+ )
817
+ );
818
+ this.setTranslationProgress(batch.length);
819
+ await this.updateDatabaseWithTranslations(result);
820
+ await this.notifyTranslationCompleted();
821
+ }
822
+ } while (queueManager.queueSize(this.currentTranslatorStrategy) > 0);
772
823
  this.resetTranslationProgress();
773
824
  this.publishTranslationProgress();
774
825
  if (this.currentTranslatorStrategy === "frontend_translations") {
@@ -787,6 +838,7 @@ var TranslationManager = class {
787
838
  continue;
788
839
  }
789
840
  if (!translation.isTranslated) {
841
+ this.addRetryableFailedText(response.tool, translation.sourceText);
790
842
  continue;
791
843
  }
792
844
  this.addSourceTextToMap(response.tool, translation.sourceText);
@@ -806,12 +858,14 @@ var TranslationManager = class {
806
858
  const eventhandler = new EventHandler(mfe);
807
859
  eventhandler.publishTranslationCompleteEvent(
808
860
  Array.from(this.sourceTexts.get(mfe) ?? /* @__PURE__ */ new Set()),
809
- Array.from(this.nonRetryableTexts)
861
+ Array.from(this.nonRetryableTexts),
862
+ Array.from(this.retryableFailedTexts.get(mfe) ?? /* @__PURE__ */ new Set())
810
863
  );
811
864
  }
812
865
  this.mfeToBeNotified.clear();
813
866
  this.sourceTexts.clear();
814
867
  this.nonRetryableTexts.clear();
868
+ this.retryableFailedTexts.clear();
815
869
  }
816
870
  setTranslationProgress(batchSize) {
817
871
  this.translationProgress.current += batchSize;
@@ -848,6 +902,11 @@ var TranslationManager = class {
848
902
  existing.add(sourceText);
849
903
  this.sourceTexts.set(tool, existing);
850
904
  }
905
+ addRetryableFailedText(tool, sourceText) {
906
+ const existing = this.retryableFailedTexts.get(tool) ?? /* @__PURE__ */ new Set();
907
+ existing.add(sourceText);
908
+ this.retryableFailedTexts.set(tool, existing);
909
+ }
851
910
  publishTranslationProgress() {
852
911
  this.progressEventHandler.publishTranslationProgressEvent(
853
912
  this.translationProgress.progress,
@@ -914,8 +973,16 @@ var Client = class {
914
973
  method: "POST",
915
974
  body: JSON.stringify(requests)
916
975
  });
976
+ const responseBody = Array.isArray(data) ? data.map((group) => ({
977
+ ...group,
978
+ translations: Array.isArray(group.translations) ? group.translations.map((t) => ({
979
+ ...t,
980
+ isTranslated: t.isTranslated ?? (!!t.translation && t.translation !== ""),
981
+ retryable: t.retryable ?? true
982
+ })) : []
983
+ })) : data;
917
984
  return {
918
- responseBody: data,
985
+ responseBody,
919
986
  success: true
920
987
  };
921
988
  } catch (error) {
@@ -992,12 +1059,55 @@ var getModelDownloadEventHandler = () => {
992
1059
  }
993
1060
  return _modelDownloadEventHandler;
994
1061
  };
1062
+ var SUPPORTED_LANGUAGES = /* @__PURE__ */ new Set([
1063
+ "ar",
1064
+ "bn",
1065
+ "bg",
1066
+ "zh",
1067
+ "hr",
1068
+ "cs",
1069
+ "da",
1070
+ "nl",
1071
+ "en",
1072
+ "fi",
1073
+ "fr",
1074
+ "de",
1075
+ "el",
1076
+ "he",
1077
+ "hi",
1078
+ "hu",
1079
+ "id",
1080
+ "it",
1081
+ "ja",
1082
+ "kn",
1083
+ "ko",
1084
+ "lt",
1085
+ "mr",
1086
+ "no",
1087
+ "pl",
1088
+ "pt",
1089
+ "ro",
1090
+ "ru",
1091
+ "sk",
1092
+ "sl",
1093
+ "es",
1094
+ "sv",
1095
+ "ta",
1096
+ "te",
1097
+ "th",
1098
+ "tr",
1099
+ "uk",
1100
+ "vi"
1101
+ ]);
995
1102
  var ChromeTranslator = class _ChromeTranslator {
996
1103
  translator;
997
1104
  constructor(translator) {
998
1105
  this.translator = translator;
999
1106
  }
1000
- static translatorReadyPromise = Promise.resolve();
1107
+ static translatorReadyPromises = /* @__PURE__ */ new Map();
1108
+ static isSupportedLanguage(lang) {
1109
+ return SUPPORTED_LANGUAGES.has(lang) || SUPPORTED_LANGUAGES.has(lang.split("-")[0] ?? "");
1110
+ }
1001
1111
  static ensureRegistry() {
1002
1112
  if (!globalThis.chromeTranslatorRegistry) {
1003
1113
  globalThis.chromeTranslatorRegistry = /* @__PURE__ */ new Map();
@@ -1019,30 +1129,31 @@ var ChromeTranslator = class _ChromeTranslator {
1019
1129
  );
1020
1130
  }
1021
1131
  static async createTranslator(source, target) {
1132
+ const key = this.generateKey(source, target);
1022
1133
  let resolveReady;
1023
- this.translatorReadyPromise = new Promise((resolve) => {
1134
+ const readyPromise = new Promise((resolve) => {
1024
1135
  resolveReady = resolve;
1025
1136
  });
1137
+ this.translatorReadyPromises.set(key, readyPromise);
1026
1138
  let isDownloading = false;
1027
1139
  const translator = await Translator.create({
1028
1140
  sourceLanguage: source,
1029
1141
  targetLanguage: target,
1030
1142
  monitor(m) {
1031
- m.addEventListener(
1032
- "downloadprogress",
1033
- (e) => {
1034
- isDownloading = true;
1035
- const progress = e.total > 0 ? Math.round(e.loaded / e.total * 100) : 0;
1036
- getModelDownloadEventHandler().publishModelDownloadProgressEvent(
1037
- e.loaded,
1038
- e.total,
1039
- progress
1040
- );
1041
- if (e.loaded === e.total) {
1042
- resolveReady();
1043
- }
1143
+ m.addEventListener("downloadprogress", (e) => {
1144
+ isDownloading = true;
1145
+ const loaded = e.loaded ?? 0;
1146
+ const total = e.total ?? 0;
1147
+ const progress = total > 0 ? Math.round(loaded / total * 100) : 0;
1148
+ getModelDownloadEventHandler().publishModelDownloadProgressEvent(
1149
+ loaded,
1150
+ total,
1151
+ progress
1152
+ );
1153
+ if (total > 0 && loaded >= total) {
1154
+ resolveReady();
1044
1155
  }
1045
- );
1156
+ });
1046
1157
  }
1047
1158
  });
1048
1159
  if (!isDownloading) {
@@ -1050,8 +1161,8 @@ var ChromeTranslator = class _ChromeTranslator {
1050
1161
  }
1051
1162
  return translator;
1052
1163
  }
1053
- static waitForReady() {
1054
- return this.translatorReadyPromise;
1164
+ static waitForReady(key) {
1165
+ return this.translatorReadyPromises.get(key) ?? Promise.resolve();
1055
1166
  }
1056
1167
  static async translate(text, targetLanguage) {
1057
1168
  this.ensureRegistry();
@@ -1059,8 +1170,17 @@ var ChromeTranslator = class _ChromeTranslator {
1059
1170
  const detector = await ChromeLanguageDetector.getInstance();
1060
1171
  const sourceLanguage = await detector.detectLanguage(text);
1061
1172
  const key = this.generateKey(sourceLanguage, targetLanguage);
1062
- let instance = globalThis.chromeTranslatorRegistry.get(key);
1063
- if (!instance) {
1173
+ if (!this.isSupportedLanguage(sourceLanguage) || !this.isSupportedLanguage(targetLanguage)) {
1174
+ return {
1175
+ translation: text,
1176
+ sourceLanguage,
1177
+ success: false,
1178
+ retryable: false,
1179
+ errorMessage: `Unsupported language pair: ${sourceLanguage} \u2192 ${targetLanguage}`
1180
+ };
1181
+ }
1182
+ const existingInstance = globalThis.chromeTranslatorRegistry.get(key);
1183
+ if (!existingInstance) {
1064
1184
  const translatorCapabilities = await Translator.availability({
1065
1185
  sourceLanguage,
1066
1186
  targetLanguage
@@ -1078,10 +1198,16 @@ var ChromeTranslator = class _ChromeTranslator {
1078
1198
  sourceLanguage,
1079
1199
  targetLanguage
1080
1200
  );
1081
- instance = new _ChromeTranslator(chromeTranslator);
1082
- globalThis.chromeTranslatorRegistry.set(key, instance);
1201
+ if (!globalThis.chromeTranslatorRegistry.has(key)) {
1202
+ globalThis.chromeTranslatorRegistry.set(
1203
+ key,
1204
+ new _ChromeTranslator(chromeTranslator)
1205
+ );
1206
+ }
1083
1207
  }
1084
- await this.waitForReady();
1208
+ const instance = globalThis.chromeTranslatorRegistry.get(key);
1209
+ await this.waitForReady(key);
1210
+ this.translatorReadyPromises.delete(key);
1085
1211
  const translation = await instance.translator?.translate(text);
1086
1212
  if (!translation) {
1087
1213
  return {
@@ -1089,7 +1215,7 @@ var ChromeTranslator = class _ChromeTranslator {
1089
1215
  sourceLanguage,
1090
1216
  success: false,
1091
1217
  errorMessage: "Translation failed",
1092
- retryable: true
1218
+ retryable: false
1093
1219
  };
1094
1220
  }
1095
1221
  return {
@@ -1099,11 +1225,12 @@ var ChromeTranslator = class _ChromeTranslator {
1099
1225
  retryable: true
1100
1226
  };
1101
1227
  } catch (error) {
1228
+ const message = error instanceof Error ? error.message : "Unknown error";
1102
1229
  return {
1103
1230
  translation: text,
1104
1231
  sourceLanguage: targetLanguage,
1105
1232
  success: false,
1106
- errorMessage: error instanceof Error ? error.message : "Unknown error",
1233
+ errorMessage: message,
1107
1234
  retryable: true
1108
1235
  };
1109
1236
  }
@@ -1260,13 +1387,22 @@ var aitFunction = async (text, translationRegistry, config2, tool) => {
1260
1387
  const existingByOriginal = await translationRegistry.get(
1261
1388
  text,
1262
1389
  targetLanguage,
1263
- tool
1390
+ tool,
1391
+ config2
1264
1392
  );
1265
1393
  if (existingByOriginal && existingByOriginal.isTranslated && existingByOriginal.translatedText && existingByOriginal.translatedText.trim() !== "") {
1266
1394
  return existingByOriginal.translatedText;
1267
1395
  }
1268
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);
1269
1404
  queueManager.add({
1405
+ id,
1270
1406
  originalText: text,
1271
1407
  targetLanguage,
1272
1408
  tool,
@@ -1330,12 +1466,17 @@ function AITranslationInnerProvider(props) {
1330
1466
  });
1331
1467
  (0, import_react.useEffect)(() => {
1332
1468
  const unsubscribe = eventHandler.current.subscribeToTranslationCompleteEvent(
1333
- async (sourceTranslatedTexts, nonRetryableTexts) => {
1469
+ async (sourceTranslatedTexts, nonRetryableTexts, retryableFailedTexts) => {
1334
1470
  try {
1335
1471
  await translationRegistry.current.populateRegistryFromDatabase();
1336
1472
  translationRegistry.current.populateNonRetryableTexts(
1337
1473
  nonRetryableTexts
1338
1474
  );
1475
+ translationRegistry.current.removeTextsFromEnqueued(
1476
+ retryableFailedTexts,
1477
+ locale,
1478
+ config2.getToolConfig().strategy
1479
+ );
1339
1480
  eventHandler.current.publishRerenderEvent(sourceTranslatedTexts);
1340
1481
  } catch (error) {
1341
1482
  console.error(
@@ -1346,7 +1487,7 @@ function AITranslationInnerProvider(props) {
1346
1487
  }
1347
1488
  );
1348
1489
  return () => unsubscribe();
1349
- }, []);
1490
+ }, [locale, config2]);
1350
1491
  (0, import_react.useEffect)(() => {
1351
1492
  const unsubscribe = eventHandler.current.subscribeToTranslationProgressEvent(
1352
1493
  (progress, current, total) => {
@@ -1364,15 +1505,15 @@ function AITranslationInnerProvider(props) {
1364
1505
  return () => unsubscribe();
1365
1506
  }, []);
1366
1507
  (0, import_react.useEffect)(() => {
1367
- if (isFetched && remoteConfig && remoteConfig[tool]) {
1368
- setConfig((prevConfig) => {
1369
- const clonedConfig = prevConfig.clone();
1370
- clonedConfig.setTargetLanguage(locale);
1371
- clonedConfig.setToolConfig(remoteConfig[tool]);
1372
- translator.current.updateConfig(clonedConfig);
1373
- return clonedConfig;
1374
- });
1375
- }
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
+ });
1376
1517
  }, [tool, remoteConfig, isFetched, locale]);
1377
1518
  config2.setTargetLanguage(locale);
1378
1519
  config2.setToolName(tool);
@@ -1429,6 +1570,7 @@ var TranslatedIcon = ({
1429
1570
  className,
1430
1571
  xmlns: "http://www.w3.org/2000/svg",
1431
1572
  "aria-hidden": "true",
1573
+ style: { marginRight: "10px", flexShrink: 0 },
1432
1574
  children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1433
1575
  "path",
1434
1576
  {
@@ -1463,12 +1605,14 @@ var AITranslateText = ({
1463
1605
  return;
1464
1606
  }
1465
1607
  if (sourceTexts.includes(text)) {
1466
- setDisplayText(await context.ait(text));
1608
+ const translatedText = await context.ait(text);
1609
+ setDisplayText(translatedText);
1610
+ setShowHighlightState(showHighlight && translatedText !== text);
1467
1611
  }
1468
1612
  }
1469
1613
  );
1470
1614
  return () => unsubscribe();
1471
- }, [context, text]);
1615
+ }, [context, text, showHighlight]);
1472
1616
  const reset = (0, import_react4.useCallback)(
1473
1617
  (displayValue = text) => {
1474
1618
  setDisplayText(displayValue);
@@ -1491,7 +1635,7 @@ var AITranslateText = ({
1491
1635
  const translatedText = await context.ait(text);
1492
1636
  if (cancelled) return;
1493
1637
  setDisplayText(translatedText);
1494
- setShowHighlightState(showHighlight);
1638
+ setShowHighlightState(showHighlight && translatedText !== text);
1495
1639
  } catch {
1496
1640
  if (cancelled) return;
1497
1641
  reset();
@@ -1503,8 +1647,8 @@ var AITranslateText = ({
1503
1647
  };
1504
1648
  }, [text, shouldTranslate, context, showHighlight, reset]);
1505
1649
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1506
- displayText,
1507
- showHighlightState && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TranslatedIcon, { ...translatedIconProps })
1650
+ showHighlightState && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TranslatedIcon, { ...translatedIconProps }),
1651
+ displayText
1508
1652
  ] });
1509
1653
  };
1510
1654