@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.
@@ -46,6 +46,9 @@ var Config = class _Config {
46
46
  setToolName(name) {
47
47
  this.toolName = name;
48
48
  }
49
+ isBackendTranslationStrategy() {
50
+ return this.ToolConfig.strategy === "backend_translations";
51
+ }
49
52
  getToolName() {
50
53
  return this.toolName;
51
54
  }
@@ -239,10 +242,10 @@ var Hash = class {
239
242
  var QueueManager = class {
240
243
  constructor() {
241
244
  if (!globalThis.__AI_TRANSLATION_FRONTEND_QUEUE__) {
242
- globalThis.__AI_TRANSLATION_FRONTEND_QUEUE__ = /* @__PURE__ */ new Set();
245
+ globalThis.__AI_TRANSLATION_FRONTEND_QUEUE__ = /* @__PURE__ */ new Map();
243
246
  }
244
247
  if (!globalThis.__AI_TRANSLATION_BACKEND_QUEUE__) {
245
- globalThis.__AI_TRANSLATION_BACKEND_QUEUE__ = /* @__PURE__ */ new Set();
248
+ globalThis.__AI_TRANSLATION_BACKEND_QUEUE__ = /* @__PURE__ */ new Map();
246
249
  }
247
250
  }
248
251
  getQueue(strategy) {
@@ -255,41 +258,20 @@ var QueueManager = class {
255
258
  return void 0;
256
259
  }
257
260
  add(entry) {
258
- this.getQueue(entry.translationStrategy)?.add(entry);
259
- }
260
- *generateBatches(strategy, config2) {
261
- const queue = this.getQueue(strategy);
262
- if (!queue) {
263
- throw new Error("Invalid translation strategy");
264
- }
265
- if (queue.size === 0) {
266
- console.warn("No translations to process");
267
- return;
268
- }
269
- const batch = config2.getToolConfig().translationBatchSize;
270
- let batches = [];
271
- for (const entry of queue.values()) {
272
- batches.push(entry);
273
- if (batches.length >= batch) {
274
- yield batches;
275
- batches = [];
276
- }
277
- }
278
- if (batches.length > 0) {
279
- queue.clear();
280
- yield batches;
281
- }
261
+ const queue = this.getQueue(entry.translationStrategy);
262
+ if (!queue || queue.has(entry.id)) return;
263
+ queue.set(entry.id, entry);
282
264
  }
283
- *generateBatchesV2(config2) {
265
+ *generateBatches(config2) {
284
266
  const queue = this.getQueue(config2.getToolConfig().strategy);
285
267
  if (!queue) throw new Error("Invalid translation strategy");
286
268
  const batchSize = config2.getToolConfig().translationBatchSize;
287
269
  while (queue.size > 0) {
288
270
  const batch = [];
289
271
  while (batch.length < batchSize && queue.size > 0) {
290
- const next = queue.values().next().value;
291
- queue.delete(next);
292
- batch.push(next);
272
+ const [key, entry] = queue.entries().next().value;
273
+ queue.delete(key);
274
+ batch.push(entry);
293
275
  }
294
276
  yield batch;
295
277
  }
@@ -410,6 +392,19 @@ var Storage = class _Storage {
410
392
  }
411
393
  }
412
394
  // ------------------------------------
395
+ // 🗑️ Clear Translation by ID
396
+ // ------------------------------------
397
+ static async deleteById(id) {
398
+ try {
399
+ const db = await _Storage.getDB();
400
+ await db.delete(_Storage.STORE_NAME, id);
401
+ return true;
402
+ } catch (error) {
403
+ console.error("[Storage] Failed to delete by id:", error);
404
+ return false;
405
+ }
406
+ }
407
+ // ------------------------------------
413
408
  // 🗑️ Clear All Translations
414
409
  // ------------------------------------
415
410
  static async clearAll() {
@@ -438,6 +433,7 @@ var TranslationRegistry = class _TranslationRegistry {
438
433
  static memoryCache = /* @__PURE__ */ new Map();
439
434
  static originalTextIndex = /* @__PURE__ */ new Map();
440
435
  static nonRetryableTexts = /* @__PURE__ */ new Set();
436
+ static enqueuedItems = /* @__PURE__ */ new Set();
441
437
  tool;
442
438
  constructor(tool) {
443
439
  this.tool = tool;
@@ -491,10 +487,42 @@ var TranslationRegistry = class _TranslationRegistry {
491
487
  isNonRetryable(key) {
492
488
  return _TranslationRegistry.nonRetryableTexts.has(key);
493
489
  }
494
- async get(text, targetLanguage, tool) {
490
+ enqueue(enqueuedKey) {
491
+ _TranslationRegistry.enqueuedItems.add(enqueuedKey);
492
+ }
493
+ isEnqueued(enqueuedKey) {
494
+ return _TranslationRegistry.enqueuedItems.has(enqueuedKey);
495
+ }
496
+ clearEnqueued() {
497
+ _TranslationRegistry.enqueuedItems.clear();
498
+ }
499
+ async removeTextsFromEnqueued(texts, targetLanguage, strategy) {
500
+ for (const text of texts) {
501
+ const id = await Storage.generateId(text, targetLanguage, this.tool);
502
+ _TranslationRegistry.enqueuedItems.delete(
503
+ this.generateEnqueuedKey(id, strategy)
504
+ );
505
+ }
506
+ }
507
+ static removeFromEnqueued(key) {
508
+ _TranslationRegistry.enqueuedItems.delete(key);
509
+ }
510
+ generateEnqueuedKey(id, strategy) {
511
+ return `${id}:${strategy}`;
512
+ }
513
+ async get(text, targetLanguage, tool, config2) {
495
514
  const key = await Hash.generateFromMultiple([text, targetLanguage, tool]);
496
- if (_TranslationRegistry.memoryCache.has(key)) {
497
- return _TranslationRegistry.memoryCache.get(key);
515
+ const cached = _TranslationRegistry.memoryCache.get(key);
516
+ if (cached) {
517
+ if (cached.translationStrategy === "frontend_translations" && config2.isBackendTranslationStrategy()) {
518
+ _TranslationRegistry.memoryCache.delete(key);
519
+ if (cached.translatedText) {
520
+ _TranslationRegistry.originalTextIndex.delete(cached.translatedText);
521
+ }
522
+ await Storage.deleteById(key);
523
+ return void 0;
524
+ }
525
+ return cached;
498
526
  }
499
527
  const translation = await Storage.getTranslation(
500
528
  text,
@@ -502,6 +530,10 @@ var TranslationRegistry = class _TranslationRegistry {
502
530
  tool
503
531
  );
504
532
  if (translation !== void 0) {
533
+ if (translation.translation_strategy === "frontend_translations" && config2.isBackendTranslationStrategy()) {
534
+ await Storage.deleteById(translation.id);
535
+ return void 0;
536
+ }
505
537
  return this.toTranslationRegistryEntry(translation);
506
538
  }
507
539
  return void 0;
@@ -516,6 +548,7 @@ var TranslationRegistry = class _TranslationRegistry {
516
548
  async clear() {
517
549
  _TranslationRegistry.memoryCache.clear();
518
550
  _TranslationRegistry.originalTextIndex.clear();
551
+ _TranslationRegistry.enqueuedItems.clear();
519
552
  await Storage.clearAll();
520
553
  return;
521
554
  }
@@ -523,6 +556,7 @@ var TranslationRegistry = class _TranslationRegistry {
523
556
  await Storage.deleteByToolAndStrategy(tool, strategy);
524
557
  _TranslationRegistry.memoryCache.clear();
525
558
  _TranslationRegistry.originalTextIndex.clear();
559
+ _TranslationRegistry.enqueuedItems.clear();
526
560
  return;
527
561
  }
528
562
  toTranslationRegistryEntry(translation) {
@@ -544,10 +578,16 @@ var TranslationRegistry = class _TranslationRegistry {
544
578
  translation.id
545
579
  );
546
580
  const newTranslation = this.toTranslationRegistryEntry(translation);
581
+ const enqueuedKey = this.generateEnqueuedKey(
582
+ translation.id,
583
+ translation.translation_strategy
584
+ );
547
585
  if (currentTranslation && !this.shouldUpdateTranslation(currentTranslation, newTranslation)) {
586
+ _TranslationRegistry.removeFromEnqueued(enqueuedKey);
548
587
  continue;
549
588
  }
550
589
  this.setTranslationToRegistry(newTranslation);
590
+ _TranslationRegistry.removeFromEnqueued(enqueuedKey);
551
591
  }
552
592
  return;
553
593
  }
@@ -597,12 +637,13 @@ var EventHandler = class {
597
637
  this.aiTranslationEvents = new SystemEvents(this.sourceEventIdentifier);
598
638
  this.toolName = toolName;
599
639
  }
600
- publishTranslationCompleteEvent(sourceTranslatedTexts, nonRetryableTexts) {
640
+ publishTranslationCompleteEvent(sourceTranslatedTexts, nonRetryableTexts, retryableFailedTexts = []) {
601
641
  this.aiTranslationEvents.publish(
602
642
  this.withEventName(TRANSLATION_COMPLETE_EVENT_NAME),
603
643
  {
604
644
  sourceTranslatedTexts,
605
- nonRetryableTexts
645
+ nonRetryableTexts,
646
+ retryableFailedTexts
606
647
  }
607
648
  );
608
649
  }
@@ -614,7 +655,14 @@ var EventHandler = class {
614
655
  detail.data?.sourceTranslatedTexts
615
656
  ) ? detail.data.sourceTranslatedTexts : [];
616
657
  const nonRetryableTexts = Array.isArray(detail.data?.nonRetryableTexts) ? detail.data.nonRetryableTexts : [];
617
- callback(sourceTranslatedTexts, nonRetryableTexts);
658
+ const retryableFailedTexts = Array.isArray(
659
+ detail.data?.retryableFailedTexts
660
+ ) ? detail.data.retryableFailedTexts : [];
661
+ callback(
662
+ sourceTranslatedTexts,
663
+ nonRetryableTexts,
664
+ retryableFailedTexts
665
+ );
618
666
  }
619
667
  );
620
668
  }
@@ -703,6 +751,7 @@ var TranslationManager = class {
703
751
  currentTranslatorStrategy;
704
752
  sourceTexts = /* @__PURE__ */ new Map();
705
753
  nonRetryableTexts = /* @__PURE__ */ new Set();
754
+ retryableFailedTexts = /* @__PURE__ */ new Map();
706
755
  progressEventHandler;
707
756
  translationProgress = {
708
757
  progress: 0,
@@ -731,19 +780,21 @@ var TranslationManager = class {
731
780
  this.translationProgress.total = queueManager.queueSize(
732
781
  this.currentTranslatorStrategy
733
782
  );
734
- for (const batch of queueManager.generateBatchesV2(
735
- this.translator.getConfig()
736
- )) {
737
- batch.forEach((entry) => this.mfeToBeNotified.add(entry.tool));
738
- const result = await this.translator.processTranslations(
739
- batch.map(
740
- (entry) => this.convertTranslationQueueEntryToTranslationRequest(entry)
741
- )
742
- );
743
- this.setTranslationProgress(batch.length);
744
- await this.updateDatabaseWithTranslations(result);
745
- await this.notifyTranslationCompleted();
746
- }
783
+ do {
784
+ for (const batch of queueManager.generateBatches(
785
+ this.translator.getConfig()
786
+ )) {
787
+ batch.forEach((entry) => this.mfeToBeNotified.add(entry.tool));
788
+ const result = await this.translator.processTranslations(
789
+ batch.map(
790
+ (entry) => this.convertTranslationQueueEntryToTranslationRequest(entry)
791
+ )
792
+ );
793
+ this.setTranslationProgress(batch.length);
794
+ await this.updateDatabaseWithTranslations(result);
795
+ await this.notifyTranslationCompleted();
796
+ }
797
+ } while (queueManager.queueSize(this.currentTranslatorStrategy) > 0);
747
798
  this.resetTranslationProgress();
748
799
  this.publishTranslationProgress();
749
800
  if (this.currentTranslatorStrategy === "frontend_translations") {
@@ -762,6 +813,7 @@ var TranslationManager = class {
762
813
  continue;
763
814
  }
764
815
  if (!translation.isTranslated) {
816
+ this.addRetryableFailedText(response.tool, translation.sourceText);
765
817
  continue;
766
818
  }
767
819
  this.addSourceTextToMap(response.tool, translation.sourceText);
@@ -781,12 +833,14 @@ var TranslationManager = class {
781
833
  const eventhandler = new EventHandler(mfe);
782
834
  eventhandler.publishTranslationCompleteEvent(
783
835
  Array.from(this.sourceTexts.get(mfe) ?? /* @__PURE__ */ new Set()),
784
- Array.from(this.nonRetryableTexts)
836
+ Array.from(this.nonRetryableTexts),
837
+ Array.from(this.retryableFailedTexts.get(mfe) ?? /* @__PURE__ */ new Set())
785
838
  );
786
839
  }
787
840
  this.mfeToBeNotified.clear();
788
841
  this.sourceTexts.clear();
789
842
  this.nonRetryableTexts.clear();
843
+ this.retryableFailedTexts.clear();
790
844
  }
791
845
  setTranslationProgress(batchSize) {
792
846
  this.translationProgress.current += batchSize;
@@ -823,6 +877,11 @@ var TranslationManager = class {
823
877
  existing.add(sourceText);
824
878
  this.sourceTexts.set(tool, existing);
825
879
  }
880
+ addRetryableFailedText(tool, sourceText) {
881
+ const existing = this.retryableFailedTexts.get(tool) ?? /* @__PURE__ */ new Set();
882
+ existing.add(sourceText);
883
+ this.retryableFailedTexts.set(tool, existing);
884
+ }
826
885
  publishTranslationProgress() {
827
886
  this.progressEventHandler.publishTranslationProgressEvent(
828
887
  this.translationProgress.progress,
@@ -889,8 +948,16 @@ var Client = class {
889
948
  method: "POST",
890
949
  body: JSON.stringify(requests)
891
950
  });
951
+ const responseBody = Array.isArray(data) ? data.map((group) => ({
952
+ ...group,
953
+ translations: Array.isArray(group.translations) ? group.translations.map((t) => ({
954
+ ...t,
955
+ isTranslated: t.isTranslated ?? (!!t.translation && t.translation !== ""),
956
+ retryable: t.retryable ?? true
957
+ })) : []
958
+ })) : data;
892
959
  return {
893
- responseBody: data,
960
+ responseBody,
894
961
  success: true
895
962
  };
896
963
  } catch (error) {
@@ -967,12 +1034,55 @@ var getModelDownloadEventHandler = () => {
967
1034
  }
968
1035
  return _modelDownloadEventHandler;
969
1036
  };
1037
+ var SUPPORTED_LANGUAGES = /* @__PURE__ */ new Set([
1038
+ "ar",
1039
+ "bn",
1040
+ "bg",
1041
+ "zh",
1042
+ "hr",
1043
+ "cs",
1044
+ "da",
1045
+ "nl",
1046
+ "en",
1047
+ "fi",
1048
+ "fr",
1049
+ "de",
1050
+ "el",
1051
+ "he",
1052
+ "hi",
1053
+ "hu",
1054
+ "id",
1055
+ "it",
1056
+ "ja",
1057
+ "kn",
1058
+ "ko",
1059
+ "lt",
1060
+ "mr",
1061
+ "no",
1062
+ "pl",
1063
+ "pt",
1064
+ "ro",
1065
+ "ru",
1066
+ "sk",
1067
+ "sl",
1068
+ "es",
1069
+ "sv",
1070
+ "ta",
1071
+ "te",
1072
+ "th",
1073
+ "tr",
1074
+ "uk",
1075
+ "vi"
1076
+ ]);
970
1077
  var ChromeTranslator = class _ChromeTranslator {
971
1078
  translator;
972
1079
  constructor(translator) {
973
1080
  this.translator = translator;
974
1081
  }
975
- static translatorReadyPromise = Promise.resolve();
1082
+ static translatorReadyPromises = /* @__PURE__ */ new Map();
1083
+ static isSupportedLanguage(lang) {
1084
+ return SUPPORTED_LANGUAGES.has(lang) || SUPPORTED_LANGUAGES.has(lang.split("-")[0] ?? "");
1085
+ }
976
1086
  static ensureRegistry() {
977
1087
  if (!globalThis.chromeTranslatorRegistry) {
978
1088
  globalThis.chromeTranslatorRegistry = /* @__PURE__ */ new Map();
@@ -994,30 +1104,31 @@ var ChromeTranslator = class _ChromeTranslator {
994
1104
  );
995
1105
  }
996
1106
  static async createTranslator(source, target) {
1107
+ const key = this.generateKey(source, target);
997
1108
  let resolveReady;
998
- this.translatorReadyPromise = new Promise((resolve) => {
1109
+ const readyPromise = new Promise((resolve) => {
999
1110
  resolveReady = resolve;
1000
1111
  });
1112
+ this.translatorReadyPromises.set(key, readyPromise);
1001
1113
  let isDownloading = false;
1002
1114
  const translator = await Translator.create({
1003
1115
  sourceLanguage: source,
1004
1116
  targetLanguage: target,
1005
1117
  monitor(m) {
1006
- m.addEventListener(
1007
- "downloadprogress",
1008
- (e) => {
1009
- isDownloading = true;
1010
- const progress = e.total > 0 ? Math.round(e.loaded / e.total * 100) : 0;
1011
- getModelDownloadEventHandler().publishModelDownloadProgressEvent(
1012
- e.loaded,
1013
- e.total,
1014
- progress
1015
- );
1016
- if (e.loaded === e.total) {
1017
- resolveReady();
1018
- }
1118
+ m.addEventListener("downloadprogress", (e) => {
1119
+ isDownloading = true;
1120
+ const loaded = e.loaded ?? 0;
1121
+ const total = e.total ?? 0;
1122
+ const progress = total > 0 ? Math.round(loaded / total * 100) : 0;
1123
+ getModelDownloadEventHandler().publishModelDownloadProgressEvent(
1124
+ loaded,
1125
+ total,
1126
+ progress
1127
+ );
1128
+ if (total > 0 && loaded >= total) {
1129
+ resolveReady();
1019
1130
  }
1020
- );
1131
+ });
1021
1132
  }
1022
1133
  });
1023
1134
  if (!isDownloading) {
@@ -1025,8 +1136,8 @@ var ChromeTranslator = class _ChromeTranslator {
1025
1136
  }
1026
1137
  return translator;
1027
1138
  }
1028
- static waitForReady() {
1029
- return this.translatorReadyPromise;
1139
+ static waitForReady(key) {
1140
+ return this.translatorReadyPromises.get(key) ?? Promise.resolve();
1030
1141
  }
1031
1142
  static async translate(text, targetLanguage) {
1032
1143
  this.ensureRegistry();
@@ -1034,8 +1145,17 @@ var ChromeTranslator = class _ChromeTranslator {
1034
1145
  const detector = await ChromeLanguageDetector.getInstance();
1035
1146
  const sourceLanguage = await detector.detectLanguage(text);
1036
1147
  const key = this.generateKey(sourceLanguage, targetLanguage);
1037
- let instance = globalThis.chromeTranslatorRegistry.get(key);
1038
- if (!instance) {
1148
+ if (!this.isSupportedLanguage(sourceLanguage) || !this.isSupportedLanguage(targetLanguage)) {
1149
+ return {
1150
+ translation: text,
1151
+ sourceLanguage,
1152
+ success: false,
1153
+ retryable: false,
1154
+ errorMessage: `Unsupported language pair: ${sourceLanguage} \u2192 ${targetLanguage}`
1155
+ };
1156
+ }
1157
+ const existingInstance = globalThis.chromeTranslatorRegistry.get(key);
1158
+ if (!existingInstance) {
1039
1159
  const translatorCapabilities = await Translator.availability({
1040
1160
  sourceLanguage,
1041
1161
  targetLanguage
@@ -1053,10 +1173,16 @@ var ChromeTranslator = class _ChromeTranslator {
1053
1173
  sourceLanguage,
1054
1174
  targetLanguage
1055
1175
  );
1056
- instance = new _ChromeTranslator(chromeTranslator);
1057
- globalThis.chromeTranslatorRegistry.set(key, instance);
1176
+ if (!globalThis.chromeTranslatorRegistry.has(key)) {
1177
+ globalThis.chromeTranslatorRegistry.set(
1178
+ key,
1179
+ new _ChromeTranslator(chromeTranslator)
1180
+ );
1181
+ }
1058
1182
  }
1059
- await this.waitForReady();
1183
+ const instance = globalThis.chromeTranslatorRegistry.get(key);
1184
+ await this.waitForReady(key);
1185
+ this.translatorReadyPromises.delete(key);
1060
1186
  const translation = await instance.translator?.translate(text);
1061
1187
  if (!translation) {
1062
1188
  return {
@@ -1064,7 +1190,7 @@ var ChromeTranslator = class _ChromeTranslator {
1064
1190
  sourceLanguage,
1065
1191
  success: false,
1066
1192
  errorMessage: "Translation failed",
1067
- retryable: true
1193
+ retryable: false
1068
1194
  };
1069
1195
  }
1070
1196
  return {
@@ -1074,11 +1200,12 @@ var ChromeTranslator = class _ChromeTranslator {
1074
1200
  retryable: true
1075
1201
  };
1076
1202
  } catch (error) {
1203
+ const message = error instanceof Error ? error.message : "Unknown error";
1077
1204
  return {
1078
1205
  translation: text,
1079
1206
  sourceLanguage: targetLanguage,
1080
1207
  success: false,
1081
- errorMessage: error instanceof Error ? error.message : "Unknown error",
1208
+ errorMessage: message,
1082
1209
  retryable: true
1083
1210
  };
1084
1211
  }
@@ -1235,13 +1362,22 @@ var aitFunction = async (text, translationRegistry, config2, tool) => {
1235
1362
  const existingByOriginal = await translationRegistry.get(
1236
1363
  text,
1237
1364
  targetLanguage,
1238
- tool
1365
+ tool,
1366
+ config2
1239
1367
  );
1240
1368
  if (existingByOriginal && existingByOriginal.isTranslated && existingByOriginal.translatedText && existingByOriginal.translatedText.trim() !== "") {
1241
1369
  return existingByOriginal.translatedText;
1242
1370
  }
1243
1371
  if (!existingByOriginal) {
1372
+ const id = await Storage.generateId(text, targetLanguage, tool);
1373
+ const strategy = config2.getToolConfig().strategy;
1374
+ const enqueuedKey = translationRegistry.generateEnqueuedKey(id, strategy);
1375
+ if (translationRegistry.isEnqueued(enqueuedKey)) {
1376
+ return text;
1377
+ }
1378
+ translationRegistry.enqueue(enqueuedKey);
1244
1379
  queueManager.add({
1380
+ id,
1245
1381
  originalText: text,
1246
1382
  targetLanguage,
1247
1383
  tool,
@@ -1305,12 +1441,17 @@ function AITranslationInnerProvider(props) {
1305
1441
  });
1306
1442
  useEffect(() => {
1307
1443
  const unsubscribe = eventHandler.current.subscribeToTranslationCompleteEvent(
1308
- async (sourceTranslatedTexts, nonRetryableTexts) => {
1444
+ async (sourceTranslatedTexts, nonRetryableTexts, retryableFailedTexts) => {
1309
1445
  try {
1310
1446
  await translationRegistry.current.populateRegistryFromDatabase();
1311
1447
  translationRegistry.current.populateNonRetryableTexts(
1312
1448
  nonRetryableTexts
1313
1449
  );
1450
+ translationRegistry.current.removeTextsFromEnqueued(
1451
+ retryableFailedTexts,
1452
+ locale,
1453
+ config2.getToolConfig().strategy
1454
+ );
1314
1455
  eventHandler.current.publishRerenderEvent(sourceTranslatedTexts);
1315
1456
  } catch (error) {
1316
1457
  console.error(
@@ -1321,7 +1462,7 @@ function AITranslationInnerProvider(props) {
1321
1462
  }
1322
1463
  );
1323
1464
  return () => unsubscribe();
1324
- }, []);
1465
+ }, [locale, config2]);
1325
1466
  useEffect(() => {
1326
1467
  const unsubscribe = eventHandler.current.subscribeToTranslationProgressEvent(
1327
1468
  (progress, current, total) => {
@@ -1339,15 +1480,15 @@ function AITranslationInnerProvider(props) {
1339
1480
  return () => unsubscribe();
1340
1481
  }, []);
1341
1482
  useEffect(() => {
1342
- if (isFetched && remoteConfig && remoteConfig[tool]) {
1343
- setConfig((prevConfig) => {
1344
- const clonedConfig = prevConfig.clone();
1345
- clonedConfig.setTargetLanguage(locale);
1346
- clonedConfig.setToolConfig(remoteConfig[tool]);
1347
- translator.current.updateConfig(clonedConfig);
1348
- return clonedConfig;
1349
- });
1350
- }
1483
+ if (!isFetched || !remoteConfig) return;
1484
+ const toolConfig = remoteConfig[tool] ?? remoteConfig;
1485
+ setConfig((prevConfig) => {
1486
+ const clonedConfig = prevConfig.clone();
1487
+ clonedConfig.setTargetLanguage(locale);
1488
+ clonedConfig.setToolConfig(toolConfig);
1489
+ translator.current.updateConfig(clonedConfig);
1490
+ return clonedConfig;
1491
+ });
1351
1492
  }, [tool, remoteConfig, isFetched, locale]);
1352
1493
  config2.setTargetLanguage(locale);
1353
1494
  config2.setToolName(tool);
@@ -1410,6 +1551,7 @@ var TranslatedIcon = ({
1410
1551
  className,
1411
1552
  xmlns: "http://www.w3.org/2000/svg",
1412
1553
  "aria-hidden": "true",
1554
+ style: { marginRight: "10px", flexShrink: 0 },
1413
1555
  children: /* @__PURE__ */ jsx2(
1414
1556
  "path",
1415
1557
  {
@@ -1444,12 +1586,14 @@ var AITranslateText = ({
1444
1586
  return;
1445
1587
  }
1446
1588
  if (sourceTexts.includes(text)) {
1447
- setDisplayText(await context.ait(text));
1589
+ const translatedText = await context.ait(text);
1590
+ setDisplayText(translatedText);
1591
+ setShowHighlightState(showHighlight && translatedText !== text);
1448
1592
  }
1449
1593
  }
1450
1594
  );
1451
1595
  return () => unsubscribe();
1452
- }, [context, text]);
1596
+ }, [context, text, showHighlight]);
1453
1597
  const reset = useCallback2(
1454
1598
  (displayValue = text) => {
1455
1599
  setDisplayText(displayValue);
@@ -1472,7 +1616,7 @@ var AITranslateText = ({
1472
1616
  const translatedText = await context.ait(text);
1473
1617
  if (cancelled) return;
1474
1618
  setDisplayText(translatedText);
1475
- setShowHighlightState(showHighlight);
1619
+ setShowHighlightState(showHighlight && translatedText !== text);
1476
1620
  } catch {
1477
1621
  if (cancelled) return;
1478
1622
  reset();
@@ -1484,8 +1628,8 @@ var AITranslateText = ({
1484
1628
  };
1485
1629
  }, [text, shouldTranslate, context, showHighlight, reset]);
1486
1630
  return /* @__PURE__ */ jsxs(Fragment, { children: [
1487
- displayText,
1488
- showHighlightState && /* @__PURE__ */ jsx3(TranslatedIcon, { ...translatedIconProps })
1631
+ showHighlightState && /* @__PURE__ */ jsx3(TranslatedIcon, { ...translatedIconProps }),
1632
+ displayText
1489
1633
  ] });
1490
1634
  };
1491
1635
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@procore/ai-translations",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Library that provides a solution to use AI to translate text into a language",
5
5
  "main": "dist/legacy/index.js",
6
6
  "types": "dist/legacy/index.d.ts",