gt-sanity 2.0.7 → 2.0.9

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.
Files changed (38) hide show
  1. package/README.md +3 -0
  2. package/dist/index.d.mts +13 -14
  3. package/dist/index.d.ts +13 -14
  4. package/dist/index.js +309 -101
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +310 -102
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +2 -2
  9. package/src/adapter/core.ts +10 -0
  10. package/src/adapter/types.ts +6 -5
  11. package/src/components/TranslationsProvider.tsx +86 -28
  12. package/src/components/page/TranslationsTable.tsx +12 -4
  13. package/src/components/shared/LanguageStatus.tsx +3 -2
  14. package/src/components/shared/SingleDocumentView.tsx +12 -4
  15. package/src/components/tab/TranslationView.tsx +35 -6
  16. package/src/configuration/baseDocumentLevelConfig/documentLevelPatch.test.ts +76 -0
  17. package/src/configuration/baseDocumentLevelConfig/documentLevelPatch.ts +27 -2
  18. package/src/configuration/baseDocumentLevelConfig/helpers/createI18nDocAndPatchMetadata.ts +12 -5
  19. package/src/configuration/baseDocumentLevelConfig/helpers/createTranslationMetadata.ts +2 -1
  20. package/src/configuration/baseDocumentLevelConfig/helpers/getOrCreateTranslationMetadata.ts +7 -4
  21. package/src/configuration/baseDocumentLevelConfig/helpers/getTranslationMetadata.ts +2 -1
  22. package/src/configuration/baseDocumentLevelConfig/helpers/patchI18nDoc.test.ts +52 -0
  23. package/src/configuration/baseDocumentLevelConfig/helpers/patchI18nDoc.ts +20 -2
  24. package/src/configuration/utils/findLatestDraft.ts +3 -1
  25. package/src/index.ts +4 -0
  26. package/src/sanity-api/findDocuments.ts +11 -5
  27. package/src/sanity-api/publishDocuments.ts +2 -1
  28. package/src/sanity-api/resolveRefs.ts +5 -4
  29. package/src/translation/checkTranslationStatus.ts +25 -6
  30. package/src/utils/__tests__/batchProcessor.test.ts +44 -0
  31. package/src/utils/__tests__/documentIds.test.ts +42 -0
  32. package/src/utils/__tests__/forEachMatchingField.test.ts +71 -1
  33. package/src/utils/__tests__/importUtils.test.ts +66 -0
  34. package/src/utils/applyDocuments.ts +56 -5
  35. package/src/utils/batchProcessor.ts +40 -1
  36. package/src/utils/documentIds.ts +49 -0
  37. package/src/utils/importUtils.ts +15 -7
  38. package/src/utils/serialize.ts +10 -6
package/dist/index.js CHANGED
@@ -35,6 +35,7 @@ class GTConfig {
35
35
  singletons;
36
36
  singletonMapping;
37
37
  ignoreFields;
38
+ dedupeFields;
38
39
  skipFields;
39
40
  translateDocuments;
40
41
  additionalStopTypes;
@@ -42,8 +43,8 @@ class GTConfig {
42
43
  additionalDeserializers;
43
44
  additionalBlockDeserializers;
44
45
  static instance;
45
- constructor(secretsNamespace, languageField, sourceLocale, locales, singletons, singletonMapping, ignoreFields, skipFields, translateDocuments, additionalStopTypes = [], additionalSerializers = {}, additionalDeserializers = {}, additionalBlockDeserializers = []) {
46
- this.secretsNamespace = secretsNamespace, this.languageField = languageField, this.sourceLocale = sourceLocale, this.locales = locales, this.singletons = singletons, this.singletonMapping = singletonMapping, this.ignoreFields = ignoreFields, this.skipFields = skipFields, this.translateDocuments = translateDocuments, this.additionalStopTypes = additionalStopTypes, this.additionalSerializers = additionalSerializers, this.additionalDeserializers = additionalDeserializers, this.additionalBlockDeserializers = additionalBlockDeserializers;
46
+ constructor(secretsNamespace, languageField, sourceLocale, locales, singletons, singletonMapping, ignoreFields, dedupeFields, skipFields, translateDocuments, additionalStopTypes = [], additionalSerializers = {}, additionalDeserializers = {}, additionalBlockDeserializers = []) {
47
+ this.secretsNamespace = secretsNamespace, this.languageField = languageField, this.sourceLocale = sourceLocale, this.locales = locales, this.singletons = singletons, this.singletonMapping = singletonMapping, this.ignoreFields = ignoreFields, this.dedupeFields = dedupeFields, this.skipFields = skipFields, this.translateDocuments = translateDocuments, this.additionalStopTypes = additionalStopTypes, this.additionalSerializers = additionalSerializers, this.additionalDeserializers = additionalDeserializers, this.additionalBlockDeserializers = additionalBlockDeserializers;
47
48
  }
48
49
  static getInstance() {
49
50
  return this.instance || (this.instance = new GTConfig(
@@ -57,13 +58,14 @@ class GTConfig {
57
58
  [],
58
59
  [],
59
60
  [],
61
+ [],
60
62
  {},
61
63
  {},
62
64
  []
63
65
  )), this.instance;
64
66
  }
65
- init(secretsNamespace, languageField, sourceLocale, locales, singletons, singletonMapping, ignoreFields, skipFields, translateDocuments, additionalStopTypes = [], additionalSerializers = {}, additionalDeserializers = {}, additionalBlockDeserializers = []) {
66
- this.secretsNamespace = secretsNamespace, this.languageField = languageField, this.sourceLocale = sourceLocale, this.locales = locales, this.singletons = singletons, this.singletonMapping = singletonMapping, this.ignoreFields = ignoreFields, this.skipFields = skipFields, this.translateDocuments = translateDocuments, this.additionalStopTypes = additionalStopTypes, this.additionalSerializers = additionalSerializers, this.additionalDeserializers = additionalDeserializers, this.additionalBlockDeserializers = additionalBlockDeserializers;
67
+ init(secretsNamespace, languageField, sourceLocale, locales, singletons, singletonMapping, ignoreFields, dedupeFields, skipFields, translateDocuments, additionalStopTypes = [], additionalSerializers = {}, additionalDeserializers = {}, additionalBlockDeserializers = []) {
68
+ this.secretsNamespace = secretsNamespace, this.languageField = languageField, this.sourceLocale = sourceLocale, this.locales = locales, this.singletons = singletons, this.singletonMapping = singletonMapping, this.ignoreFields = ignoreFields, this.dedupeFields = dedupeFields, this.skipFields = skipFields, this.translateDocuments = translateDocuments, this.additionalStopTypes = additionalStopTypes, this.additionalSerializers = additionalSerializers, this.additionalDeserializers = additionalDeserializers, this.additionalBlockDeserializers = additionalBlockDeserializers;
67
69
  }
68
70
  getSecretsNamespace() {
69
71
  return this.secretsNamespace;
@@ -86,6 +88,9 @@ class GTConfig {
86
88
  getIgnoreFields() {
87
89
  return this.ignoreFields;
88
90
  }
91
+ getDedupeFields() {
92
+ return this.dedupeFields;
93
+ }
89
94
  getSkipFields() {
90
95
  return this.skipFields;
91
96
  }
@@ -629,14 +634,57 @@ function deleteMatchingFields(documentId, document2, fields) {
629
634
  for (const { parent, index } of arrayRemovals)
630
635
  parent.splice(index, 1);
631
636
  }
632
- function applyDocuments(documentId, sourceDocument, targetDocument, ignore, skip = []) {
637
+ function applyDocuments(documentId, sourceDocument, targetDocument, ignore, skip = [], dedupe = [], localeId) {
633
638
  const mergedDocument = JSON.parse(JSON.stringify(sourceDocument)), clonedTarget = JSON.parse(JSON.stringify(targetDocument));
634
639
  for (const [key, value] of Object.entries(clonedTarget))
635
640
  mergedDocument[key] = value;
636
641
  return forEachMatchingField(documentId, sourceDocument, ignore, (result) => {
637
642
  JSONPointer__default.default.set(mergedDocument, result.pointer, result.value);
643
+ }), forEachMatchingField(documentId, sourceDocument, dedupe, (result) => {
644
+ JSONPointer__default.default.set(
645
+ mergedDocument,
646
+ result.pointer,
647
+ dedupeFieldValue(result.value, localeId)
648
+ );
638
649
  }), deleteMatchingFields(documentId, mergedDocument, skip), mergedDocument;
639
650
  }
651
+ function dedupeFieldValue(value, localeId) {
652
+ return localeId ? typeof value == "string" ? appendLocaleSuffix(value, localeId) : value && typeof value == "object" && !Array.isArray(value) && typeof value.current == "string" ? {
653
+ ...value,
654
+ current: appendLocaleSuffix(value.current, localeId)
655
+ } : value : value;
656
+ }
657
+ function appendLocaleSuffix(value, localeId) {
658
+ if (!value) return value;
659
+ const suffix = createLocaleSuffix(localeId);
660
+ return !suffix || value.endsWith(suffix) ? value : `${value}${suffix}`;
661
+ }
662
+ function createLocaleSuffix(localeId) {
663
+ const normalized = localeId.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
664
+ return normalized ? `-${normalized}` : "";
665
+ }
666
+ function getPublishedId(documentId) {
667
+ return documentId.startsWith("drafts.") ? documentId.slice(7) : documentId;
668
+ }
669
+ function getDocumentPublishedId(document2) {
670
+ return getPublishedId(document2._id);
671
+ }
672
+ function dedupeDocumentsPreferDraft(documents) {
673
+ const byPublishedId = /* @__PURE__ */ new Map();
674
+ for (const document2 of documents) {
675
+ const publishedId = getDocumentPublishedId(document2);
676
+ (!byPublishedId.get(publishedId) || document2._id.startsWith("drafts.")) && byPublishedId.set(publishedId, document2);
677
+ }
678
+ return Array.from(byPublishedId.values());
679
+ }
680
+ function createTranslationStatusKey(branchId, documentId, versionId, localeId) {
681
+ const publishedId = getPublishedId(documentId);
682
+ return branchId ? `${branchId}:${publishedId}:${versionId}:${localeId}` : `${publishedId}:${versionId}:${localeId}`;
683
+ }
684
+ function createStableTranslationKey(branchId, documentId, localeId) {
685
+ const publishedId = getPublishedId(documentId);
686
+ return branchId ? `${branchId}:${publishedId}:${localeId}` : `${publishedId}:${localeId}`;
687
+ }
640
688
  function deserializeDocument(document2) {
641
689
  const deserializers = merge__default.default(
642
690
  { types: {} },
@@ -660,7 +708,8 @@ function serializeDocument(document2, schema2, baseLanguage) {
660
708
  pluginConfig.getAdditionalSerializers()
661
709
  ), docToSerialize = stripIgnoredFields(
662
710
  document2,
663
- pluginConfig.getIgnoreFields()
711
+ pluginConfig.getIgnoreFields(),
712
+ pluginConfig.getDedupeFields()
664
713
  );
665
714
  return BaseDocumentSerializer(schema2).serializeDocument(
666
715
  docToSerialize,
@@ -670,13 +719,14 @@ function serializeDocument(document2, schema2, baseLanguage) {
670
719
  serializers
671
720
  );
672
721
  }
673
- function stripIgnoredFields(document2, ignoreFields) {
674
- if (ignoreFields.length === 0) return document2;
722
+ function stripIgnoredFields(document2, ignoreFields, dedupeFields) {
723
+ const fieldsToStrip = [...ignoreFields, ...dedupeFields];
724
+ if (fieldsToStrip.length === 0) return document2;
675
725
  const strippedDoc = JSON.parse(JSON.stringify(document2));
676
726
  return deleteMatchingFields(
677
- document2._id.replace("drafts.", ""),
727
+ getPublishedId(document2._id),
678
728
  strippedDoc,
679
- ignoreFields
729
+ fieldsToStrip
680
730
  ), strippedDoc;
681
731
  }
682
732
  async function uploadFiles(documents, secrets) {
@@ -755,9 +805,19 @@ async function downloadTranslations(files, secrets, maxRetries = 3, retryDelay =
755
805
  async function checkTranslationStatus(fileQueryData, downloadStatus, secrets) {
756
806
  overrideConfig(secrets);
757
807
  try {
758
- const currentQueryData = fileQueryData.filter(
759
- (item) => !downloadStatus.downloaded.has(`${item.fileId}:${item.locale}`) && !downloadStatus.failed.has(`${item.fileId}:${item.locale}`) && !downloadStatus.skipped.has(`${item.fileId}:${item.locale}`)
760
- );
808
+ const currentQueryData = fileQueryData.filter((item) => {
809
+ const statusKey = createTranslationStatusKey(
810
+ item.branchId,
811
+ item.fileId,
812
+ item.versionId,
813
+ item.locale
814
+ ), stableKey = createStableTranslationKey(
815
+ item.branchId,
816
+ item.fileId,
817
+ item.locale
818
+ );
819
+ return !downloadStatus.downloaded.has(statusKey) && !downloadStatus.downloaded.has(stableKey) && !downloadStatus.failed.has(statusKey) && !downloadStatus.failed.has(stableKey) && !downloadStatus.skipped.has(statusKey) && !downloadStatus.skipped.has(stableKey);
820
+ });
761
821
  return currentQueryData.length === 0 ? !0 : ((await gt.queryFileData({
762
822
  translatedFiles: currentQueryData
763
823
  })).translatedFiles || []).filter(
@@ -768,7 +828,7 @@ async function checkTranslationStatus(fileQueryData, downloadStatus, secrets) {
768
828
  }
769
829
  }
770
830
  const findLatestDraft = (documentId, client) => {
771
- const query = "*[_id == $id || _id == $draftId]", params = { id: documentId, draftId: `drafts.${documentId}` };
831
+ const publishedId = getPublishedId(documentId), query = "*[_id == $id || _id == $draftId]", params = { id: publishedId, draftId: `drafts.${publishedId}` };
772
832
  return client.fetch(query, params).then(
773
833
  (docs) => docs.find((doc) => doc._id.startsWith("drafts.")) ?? docs[0]
774
834
  );
@@ -780,19 +840,25 @@ function randomKey() {
780
840
  return Math.random().toString(36).slice(2, 10);
781
841
  }
782
842
  async function createI18nDocAndPatchMetadata(sourceDocument, translatedDoc, localeId, client, translationMetadata, sourceDocumentId, languageField = "language") {
843
+ const publishedSourceDocumentId = getPublishedId(sourceDocumentId);
783
844
  translatedDoc[languageField] = localeId;
784
845
  const existingLocaleKey = translationMetadata.translations.find(
785
846
  (translation) => translation.language === localeId
786
847
  ), operation = existingLocaleKey ? "replace" : "after", location = existingLocaleKey ? `translations[language == "${localeId}"]` : "translations[-1]", { _updatedAt, _createdAt, ...rest } = translatedDoc, appliedDocument = applyDocuments(
787
- sourceDocumentId,
848
+ publishedSourceDocumentId,
788
849
  sourceDocument,
789
850
  rest,
790
851
  pluginConfig.getIgnoreFields(),
791
- pluginConfig.getSkipFields()
792
- ), isSingleton = pluginConfig.getSingletons().includes(sourceDocumentId);
852
+ pluginConfig.getSkipFields(),
853
+ pluginConfig.getDedupeFields(),
854
+ localeId
855
+ ), isSingleton = pluginConfig.getSingletons().includes(publishedSourceDocumentId);
793
856
  let createDocumentPromise;
794
857
  if (isSingleton) {
795
- const translatedDocId = pluginConfig.getSingletonMapping()(sourceDocumentId, localeId);
858
+ const translatedDocId = pluginConfig.getSingletonMapping()(
859
+ publishedSourceDocumentId,
860
+ localeId
861
+ );
796
862
  createDocumentPromise = client.create({
797
863
  ...appliedDocument,
798
864
  _type: rest._type,
@@ -804,7 +870,7 @@ async function createI18nDocAndPatchMetadata(sourceDocument, translatedDoc, loca
804
870
  _type: rest._type,
805
871
  _id: "drafts."
806
872
  });
807
- const doc = await createDocumentPromise, _ref = doc._id.replace("drafts.", "");
873
+ const doc = await createDocumentPromise, _ref = getPublishedId(doc._id);
808
874
  await client.transaction().patch(
809
875
  translationMetadata._id,
810
876
  (p) => p.insert(operation, location, [
@@ -825,12 +891,12 @@ async function createI18nDocAndPatchMetadata(sourceDocument, translatedDoc, loca
825
891
  ).commit();
826
892
  }
827
893
  const getOrCreateTranslationMetadata = async (documentId, baseDocument, client, baseLanguage) => {
828
- const existingMetadata = await client.fetch(
894
+ const publishedId = getPublishedId(documentId), existingMetadata = await client.fetch(
829
895
  `*[
830
896
  _type == 'translation.metadata' &&
831
897
  translations[language == $baseLanguage][0].value._ref == $id
832
898
  ][0]`,
833
- { baseLanguage, id: documentId.replace("drafts.", "") }
899
+ { baseLanguage, id: publishedId }
834
900
  );
835
901
  if (existingMetadata)
836
902
  return existingMetadata;
@@ -840,7 +906,7 @@ const getOrCreateTranslationMetadata = async (documentId, baseDocument, client,
840
906
  _type: "internationalizedArrayReferenceValue",
841
907
  value: {
842
908
  _type: "reference",
843
- _ref: baseDocument._id.replace("drafts.", "")
909
+ _ref: getPublishedId(baseDocument._id)
844
910
  }
845
911
  };
846
912
  baseDocument._id.startsWith("drafts.") && (baseLangEntry.value = {
@@ -854,7 +920,7 @@ const getOrCreateTranslationMetadata = async (documentId, baseDocument, client,
854
920
  });
855
921
  try {
856
922
  return await client.createIfNotExists({
857
- _id: `translation.metadata.${documentId.replace("drafts.", "")}`,
923
+ _id: `translation.metadata.${publishedId}`,
858
924
  _type: "translation.metadata",
859
925
  translations: [baseLangEntry]
860
926
  });
@@ -864,14 +930,14 @@ const getOrCreateTranslationMetadata = async (documentId, baseDocument, client,
864
930
  _type == 'translation.metadata' &&
865
931
  translations[language == $baseLanguage][0].value._ref == $id
866
932
  ][0]`,
867
- { baseLanguage, id: documentId.replace("drafts.", "") }
933
+ { baseLanguage, id: publishedId }
868
934
  );
869
935
  if (metadata)
870
936
  return metadata;
871
937
  throw error;
872
938
  }
873
939
  }, SYSTEM_FIELDS = ["_id", "_rev", "_updatedAt", "language"], isSystemField = (field) => SYSTEM_FIELDS.includes(field);
874
- async function patchI18nDoc(sourceDocumentId, i18nDocId, sourceDocument, mergedDocument, translatedFields, client) {
940
+ async function patchI18nDoc(sourceDocumentId, i18nDocId, sourceDocument, mergedDocument, translatedFields, client, existingDocument) {
875
941
  const cleanedMerge = {};
876
942
  Object.entries(mergedDocument).forEach(([key, value]) => {
877
943
  key in translatedFields && //don't overwrite any existing system values on the i18n doc
@@ -887,8 +953,15 @@ async function patchI18nDoc(sourceDocumentId, i18nDocId, sourceDocument, mergedD
887
953
  cleanedMerge,
888
954
  pluginConfig.getIgnoreFields(),
889
955
  pluginConfig.getSkipFields()
890
- );
891
- await client.patch(i18nDocId, { set: appliedDocument }).commit();
956
+ ), dedupeFields = pluginConfig.getDedupeFields();
957
+ deleteMatchingFields(sourceDocumentId, appliedDocument, dedupeFields), existingDocument && forEachMatchingField(
958
+ sourceDocumentId,
959
+ existingDocument,
960
+ dedupeFields,
961
+ (result) => {
962
+ JSONPointer__default.default.set(appliedDocument, result.pointer, result.value);
963
+ }
964
+ ), await client.patch(i18nDocId, { set: appliedDocument }).commit();
892
965
  }
893
966
  const documentLevelPatch = async (docInfo, translatedFields, localeId, client, languageField = "language", mergeWithTargetLocale = !1) => {
894
967
  const baseLanguage = pluginConfig.getSourceLocale();
@@ -898,12 +971,12 @@ const documentLevelPatch = async (docInfo, translatedFields, localeId, client, l
898
971
  docInfo.versionId,
899
972
  client
900
973
  )), baseDoc || (baseDoc = await findLatestDraft(docInfo.documentId, client));
901
- const translationMetadata = await getOrCreateTranslationMetadata(
974
+ const i18nDocId = (await getOrCreateTranslationMetadata(
902
975
  docInfo.documentId,
903
976
  baseDoc,
904
977
  client,
905
978
  baseLanguage
906
- ), i18nDocId = translationMetadata.translations.find((translation) => translation.language === localeId)?.value?._ref;
979
+ )).translations.find((translation) => translation.language === localeId)?.value?._ref;
907
980
  i18nDocId && (i18nDoc = await findLatestDraft(i18nDocId, client)), mergeWithTargetLocale && i18nDoc ? baseDoc = i18nDoc : docInfo.documentId && docInfo.versionId && (baseDoc = await findDocumentAtRevision(
908
981
  docInfo.documentId,
909
982
  docInfo.versionId,
@@ -913,22 +986,46 @@ const documentLevelPatch = async (docInfo, translatedFields, localeId, client, l
913
986
  translatedFields,
914
987
  baseDoc
915
988
  );
916
- i18nDoc ? await patchI18nDoc(
917
- docInfo.documentId,
918
- i18nDoc._id,
919
- baseDoc,
920
- merged,
921
- translatedFields,
922
- client
923
- ) : await createI18nDocAndPatchMetadata(
924
- baseDoc,
925
- merged,
926
- localeId,
927
- client,
928
- translationMetadata,
929
- docInfo.documentId,
930
- languageField
931
- );
989
+ if (i18nDoc)
990
+ await patchI18nDoc(
991
+ docInfo.documentId,
992
+ i18nDoc._id,
993
+ baseDoc,
994
+ merged,
995
+ translatedFields,
996
+ client,
997
+ i18nDoc
998
+ );
999
+ else {
1000
+ const freshTranslationMetadata = await getOrCreateTranslationMetadata(
1001
+ docInfo.documentId,
1002
+ baseDoc,
1003
+ client,
1004
+ baseLanguage
1005
+ ), freshI18nDocId = freshTranslationMetadata.translations.find((translation) => translation.language === localeId)?.value?._ref;
1006
+ if (freshI18nDocId) {
1007
+ const freshI18nDoc = await findLatestDraft(freshI18nDocId, client);
1008
+ await patchI18nDoc(
1009
+ docInfo.documentId,
1010
+ freshI18nDoc._id,
1011
+ baseDoc,
1012
+ merged,
1013
+ translatedFields,
1014
+ client,
1015
+ freshI18nDoc
1016
+ );
1017
+ return;
1018
+ }
1019
+ await createI18nDocAndPatchMetadata(
1020
+ baseDoc,
1021
+ merged,
1022
+ localeId,
1023
+ client,
1024
+ freshTranslationMetadata,
1025
+ docInfo.documentId,
1026
+ languageField
1027
+ );
1028
+ }
932
1029
  };
933
1030
  async function importDocument(docInfo, localeId, document2, context, mergeWithTargetLocale = !1) {
934
1031
  const { client } = context, deserialized = deserializeDocument(document2);
@@ -969,10 +1066,12 @@ async function resolveTranslatedReferences(references, locale, client) {
969
1066
  return translatedRefs;
970
1067
  const sourceLocale = pluginConfig.getSourceLocale(), translationPairs = await client.fetch(`*[_type == "translation.metadata" && count(translations[language == $sourceLocale && value._ref in $refIds]) > 0] {
971
1068
  "originalRef": translations[language == $sourceLocale][0].value._ref,
972
- "translatedRef": translations[language == $locale][0].value._ref
973
- }[defined(originalRef) && defined(translatedRef)]`, { refIds, sourceLocale, locale });
974
- for (const { originalRef, translatedRef } of translationPairs)
1069
+ "translatedRefs": translations[language == $locale].value._ref
1070
+ }[defined(originalRef) && count(translatedRefs) > 0]`, { refIds, sourceLocale, locale });
1071
+ for (const { originalRef, translatedRefs: localeRefs } of translationPairs) {
1072
+ const translatedRef = localeRefs[localeRefs.length - 1];
975
1073
  translatedRefs.set(originalRef, translatedRef);
1074
+ }
976
1075
  return translatedRefs;
977
1076
  }
978
1077
  function updateDocumentReferences(doc, translatedRefs) {
@@ -994,34 +1093,50 @@ function updateReferencesRecursive(obj, translatedRefs) {
994
1093
  }), updated;
995
1094
  }
996
1095
  async function findTranslatedDocumentForLocale(sourceDocumentId, localeId, client) {
997
- const cleanDocId = sourceDocumentId.replace("drafts.", "");
998
- return await client.fetch(`*[
1096
+ const cleanDocId = getPublishedId(sourceDocumentId), translatedDocIds = await client.fetch(`*[
999
1097
  _type == "translation.metadata" &&
1000
1098
  (
1001
1099
  translations[language == $sourceLocale][0].value._ref == $cleanDocId
1002
1100
  ) &&
1003
1101
  defined(translations[language == $localeId])
1004
- ][0].translations[language == $localeId][0].value->`, {
1102
+ ][0].translations[language == $localeId].value._ref`, {
1005
1103
  sourceLocale: pluginConfig.getSourceLocale(),
1006
1104
  cleanDocId,
1007
1105
  localeId
1008
- }) || null;
1106
+ }), translatedDocId = translatedDocIds?.[translatedDocIds.length - 1];
1107
+ return translatedDocId && await findLatestDraft(translatedDocId, client) || null;
1009
1108
  }
1010
1109
  async function findDocument(documentId, client) {
1011
1110
  const query = "*[_id == $id]", params = { id: documentId };
1012
1111
  return (await client.fetch(query, params))[0] || null;
1013
1112
  }
1014
1113
  async function processBatch(items, processor, options = {}) {
1015
- const { batchSize = 20, onProgress, onItemSuccess, onItemFailure } = options;
1114
+ const {
1115
+ batchSize = 20,
1116
+ getConcurrencyKey,
1117
+ onProgress,
1118
+ onItemSuccess,
1119
+ onItemFailure
1120
+ } = options;
1016
1121
  let successCount = 0, failureCount = 0;
1017
1122
  const successfulItems = [], failedItems = [];
1018
1123
  for (let i = 0; i < items.length; i += batchSize) {
1019
- const batch = items.slice(i, i + batchSize), batchPromises = batch.map(async (item) => {
1124
+ const batch = items.slice(i, i + batchSize), pendingByKey = /* @__PURE__ */ new Map(), batchPromises = batch.map(async (item) => {
1125
+ const concurrencyKey = getConcurrencyKey?.(item), pending = concurrencyKey ? pendingByKey.get(concurrencyKey) : void 0;
1126
+ let release = () => {
1127
+ };
1128
+ const current = new Promise((resolve) => {
1129
+ release = resolve;
1130
+ });
1131
+ concurrencyKey && pendingByKey.set(concurrencyKey, current);
1020
1132
  try {
1133
+ pending && await pending;
1021
1134
  const result = await processor(item);
1022
1135
  return onItemSuccess?.(item, result), { success: !0, item, result };
1023
1136
  } catch (error) {
1024
1137
  return onItemFailure?.(item, error), { success: !1, item, error };
1138
+ } finally {
1139
+ release(), concurrencyKey && pendingByKey.get(concurrencyKey) === current && pendingByKey.delete(concurrencyKey);
1025
1140
  }
1026
1141
  });
1027
1142
  (await Promise.all(batchPromises)).forEach((result) => {
@@ -1044,6 +1159,11 @@ async function processImportBatch(items, options = {}) {
1044
1159
  ), item.key),
1045
1160
  {
1046
1161
  ...options,
1162
+ getConcurrencyKey: (item) => createStableTranslationKey(
1163
+ void 0,
1164
+ item.docInfo.documentId,
1165
+ item.locale
1166
+ ),
1047
1167
  onItemSuccess: (item, key) => {
1048
1168
  successfulImports.push(key), options.onItemSuccess?.(item, key);
1049
1169
  },
@@ -1059,15 +1179,25 @@ async function processImportBatch(items, options = {}) {
1059
1179
  };
1060
1180
  }
1061
1181
  async function getReadyFilesForImport(translationStatuses, options = {}) {
1062
- const { filterReadyFiles = () => !0 } = options, readyFiles = [];
1182
+ const { filterReadyFiles = () => !0 } = options, readyFilesByDocumentLocale = /* @__PURE__ */ new Map();
1063
1183
  for (const [key, status] of translationStatuses.entries())
1064
- status.isReady && filterReadyFiles(key, status) && readyFiles.push({
1065
- fileId: status.fileData.fileId,
1066
- versionId: status.fileData.versionId,
1067
- branchId: status.fileData.branchId,
1068
- locale: status.fileData.locale
1069
- });
1070
- return readyFiles;
1184
+ if (status.isReady && status.fileData && filterReadyFiles(key, status)) {
1185
+ const fileData = {
1186
+ fileId: getPublishedId(status.fileData.fileId),
1187
+ versionId: status.fileData.versionId,
1188
+ branchId: status.fileData.branchId,
1189
+ locale: status.fileData.locale
1190
+ };
1191
+ readyFilesByDocumentLocale.set(
1192
+ createStableTranslationKey(
1193
+ fileData.branchId,
1194
+ fileData.fileId,
1195
+ fileData.locale
1196
+ ),
1197
+ fileData
1198
+ );
1199
+ }
1200
+ return Array.from(readyFilesByDocumentLocale.values());
1071
1201
  }
1072
1202
  async function importTranslations(readyFiles, secrets, translationContext, options = {}) {
1073
1203
  if (readyFiles.length === 0)
@@ -1099,7 +1229,7 @@ async function publishDocument(documentId, client) {
1099
1229
  {
1100
1230
  actionType: "sanity.action.document.publish",
1101
1231
  draftId: documentId,
1102
- publishedId: documentId.replace("drafts.", "")
1232
+ publishedId: getPublishedId(documentId)
1103
1233
  },
1104
1234
  {}
1105
1235
  );
@@ -1147,9 +1277,13 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1147
1277
  downloaded: /* @__PURE__ */ new Set(),
1148
1278
  failed: /* @__PURE__ */ new Set(),
1149
1279
  skipped: /* @__PURE__ */ new Set()
1150
- }), [translationStatuses, setTranslationStatuses] = o.useState(/* @__PURE__ */ new Map()), [isRefreshing, setIsRefreshing] = o.useState(!1), client = useClient(), schema2 = sanity.useSchema(), translationContext = { client, schema: schema2 }, toast = ui.useToast(), { loading: loadingSecrets, secrets } = useSecrets(
1280
+ }), downloadStatusRef = o.useRef(downloadStatus), [translationStatuses, setTranslationStatuses] = o.useState(/* @__PURE__ */ new Map()), [isRefreshing, setIsRefreshing] = o.useState(!1), client = useClient(), schema2 = sanity.useSchema(), translationContext = { client, schema: schema2 }, toast = ui.useToast(), { loading: loadingSecrets, secrets } = useSecrets(
1151
1281
  pluginConfig.getSecretsNamespace()
1152
- ), [branchId, setBranchId] = o.useState(void 0), fetchDocuments = o.useCallback(async () => {
1282
+ ), [branchId, setBranchId] = o.useState(void 0);
1283
+ o.useEffect(() => {
1284
+ downloadStatusRef.current = downloadStatus;
1285
+ }, [downloadStatus]);
1286
+ const fetchDocuments = o.useCallback(async () => {
1153
1287
  setLoadingDocuments(!0);
1154
1288
  try {
1155
1289
  if (singleDocument) {
@@ -1160,7 +1294,7 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1160
1294
  let query;
1161
1295
  filterConditions.length === 0 ? query = `*[!(_type in ["system.group"]) && !(_id in path("_.**")) && ${languageFilter}]` : query = `*[!(_type in ["system.group"]) && !(_id in path("_.**")) && (${filterConditions.join(" || ")}) && ${languageFilter}]`;
1162
1296
  const docs = await client.fetch(query);
1163
- setDocuments(docs);
1297
+ setDocuments(dedupeDocumentsPreferDraft(docs));
1164
1298
  } catch {
1165
1299
  toast.push({
1166
1300
  title: "Error fetching documents",
@@ -1185,9 +1319,7 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1185
1319
  }, [secrets]), fetchExistingTranslations = o.useCallback(async () => {
1186
1320
  if (!(!documents.length || !locales.length))
1187
1321
  try {
1188
- const sourceLocale = pluginConfig.getSourceLocale(), availableLocaleIds = locales.filter((locale) => locale.enabled !== !1).map((locale) => locale.localeId), documentIds = documents.map(
1189
- (doc) => doc._id?.replace("drafts.", "") || doc._id
1190
- ), existingMetadata = await client.fetch(`*[
1322
+ const sourceLocale = pluginConfig.getSourceLocale(), availableLocaleIds = locales.filter((locale) => locale.enabled !== !1).map((locale) => locale.localeId), documentIds = documents.map(getDocumentPublishedId), existingMetadata = await client.fetch(`*[
1191
1323
  _type == 'translation.metadata' &&
1192
1324
  translations[language == $sourceLocale][0].value._ref in $documentIds
1193
1325
  ] {
@@ -1200,7 +1332,13 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1200
1332
  }), existing = /* @__PURE__ */ new Set();
1201
1333
  existingMetadata.forEach((metadata) => {
1202
1334
  metadata.existingTranslations?.forEach((localeId) => {
1203
- localeId !== sourceLocale && existing.add(`${metadata.sourceDocId}:${localeId}`);
1335
+ localeId !== sourceLocale && existing.add(
1336
+ createStableTranslationKey(
1337
+ void 0,
1338
+ metadata.sourceDocId,
1339
+ localeId
1340
+ )
1341
+ );
1204
1342
  });
1205
1343
  }), setExistingTranslations(existing);
1206
1344
  } catch (error) {
@@ -1224,7 +1362,7 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1224
1362
  );
1225
1363
  return {
1226
1364
  info: {
1227
- documentId: doc._id?.replace("drafts.", "") || doc._id,
1365
+ documentId: getDocumentPublishedId(doc),
1228
1366
  versionId: doc._rev
1229
1367
  },
1230
1368
  serializedDocument: serialized
@@ -1329,7 +1467,11 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1329
1467
  return existingMetadata.forEach((metadata) => {
1330
1468
  metadata.existingTranslations?.forEach((localeId) => {
1331
1469
  localeId !== sourceLocale && existing.add(
1332
- `${branchId2}:${metadata.sourceDocId}:${metadata._rev}:${localeId}`
1470
+ createStableTranslationKey(
1471
+ branchId2,
1472
+ metadata.sourceDocId,
1473
+ localeId
1474
+ )
1333
1475
  );
1334
1476
  });
1335
1477
  }), existing;
@@ -1339,14 +1481,18 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1339
1481
  if (!(!secrets || documents.length === 0 || !branchId)) {
1340
1482
  setIsBusy(!0);
1341
1483
  try {
1342
- const availableLocaleIds = locales.filter((locale) => locale.enabled !== !1).map((locale) => locale.localeId), documentIds = documents.map(
1343
- (doc) => doc._id?.replace("drafts.", "") || doc._id
1344
- ), existingTranslations2 = await getExistingTranslations(
1484
+ const availableLocaleIds = locales.filter((locale) => locale.enabled !== !1).map((locale) => locale.localeId), documentIds = documents.map(getDocumentPublishedId), existingTranslations2 = await getExistingTranslations(
1345
1485
  documentIds,
1346
1486
  availableLocaleIds,
1347
1487
  branchId
1348
1488
  ), readyFiles = await getReadyFilesForImport(translationStatuses, {
1349
- filterReadyFiles: (key) => !existingTranslations2.has(key)
1489
+ filterReadyFiles: (_key, status) => !existingTranslations2.has(
1490
+ createStableTranslationKey(
1491
+ branchId,
1492
+ status.fileData.fileId,
1493
+ status.fileData.locale
1494
+ )
1495
+ )
1350
1496
  });
1351
1497
  if (readyFiles.length === 0) {
1352
1498
  toast.push({
@@ -1425,7 +1571,7 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1425
1571
  const availableLocaleIds = locales.filter((locale) => locale.enabled !== !1).map((locale) => locale.localeId), fileQueryData = [];
1426
1572
  for (const doc of documents)
1427
1573
  for (const localeId of availableLocaleIds) {
1428
- const documentId = doc._id?.replace("drafts.", "") || doc._id;
1574
+ const documentId = getDocumentPublishedId(doc);
1429
1575
  fileQueryData.push({
1430
1576
  versionId: doc._rev,
1431
1577
  fileId: documentId,
@@ -1435,19 +1581,29 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1435
1581
  }
1436
1582
  const readyTranslations = await checkTranslationStatus(
1437
1583
  fileQueryData,
1438
- downloadStatus,
1584
+ downloadStatusRef.current,
1439
1585
  secrets
1440
1586
  );
1441
1587
  setTranslationStatuses((prevStatuses) => {
1442
1588
  const newStatuses = /* @__PURE__ */ new Map();
1443
1589
  for (const doc of documents)
1444
1590
  for (const localeId of availableLocaleIds) {
1445
- const documentId = doc._id?.replace("drafts.", "") || doc._id, versionId = doc._rev, key = `${branchId}:${documentId}:${versionId}:${localeId}`;
1591
+ const documentId = getDocumentPublishedId(doc), versionId = doc._rev, key = createTranslationStatusKey(
1592
+ branchId,
1593
+ documentId,
1594
+ versionId,
1595
+ localeId
1596
+ );
1446
1597
  newStatuses.set(key, { progress: 0, isReady: !1 });
1447
1598
  }
1448
1599
  if (Array.isArray(readyTranslations))
1449
1600
  for (const translation of readyTranslations) {
1450
- const key = `${branchId}:${translation.fileId}:${translation.versionId}:${translation.locale}`;
1601
+ const key = createTranslationStatusKey(
1602
+ branchId,
1603
+ translation.fileId,
1604
+ translation.versionId,
1605
+ translation.locale
1606
+ );
1451
1607
  newStatuses.set(key, {
1452
1608
  progress: 100,
1453
1609
  isReady: !0,
@@ -1478,7 +1634,12 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1478
1634
  }, [secrets, documents, locales, branchId]), handleImportDocument = o.useCallback(
1479
1635
  async (documentId, versionId, localeId) => {
1480
1636
  if (!secrets) return;
1481
- const key = `${branchId}:${documentId}:${versionId}:${localeId}`, status = translationStatuses.get(key);
1637
+ const key = createTranslationStatusKey(
1638
+ branchId,
1639
+ documentId,
1640
+ versionId,
1641
+ localeId
1642
+ ), status = translationStatuses.get(key);
1482
1643
  if (!status?.isReady || !status.fileData) {
1483
1644
  toast.push({
1484
1645
  title: `Translation not ready for ${documentId} (${localeId})`,
@@ -1488,7 +1649,7 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1488
1649
  return;
1489
1650
  }
1490
1651
  const document2 = documents.find(
1491
- (doc) => (doc._id?.replace("drafts.", "") || doc._id) === documentId
1652
+ (doc) => getDocumentPublishedId(doc) === getPublishedId(documentId)
1492
1653
  );
1493
1654
  if (!document2) {
1494
1655
  toast.push({
@@ -1513,7 +1674,7 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1513
1674
  if (downloadedFiles.length > 0)
1514
1675
  try {
1515
1676
  const docInfo = {
1516
- documentId,
1677
+ documentId: getPublishedId(documentId),
1517
1678
  versionId: document2._rev
1518
1679
  };
1519
1680
  await importDocument(
@@ -1525,7 +1686,12 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1525
1686
  ), setDownloadStatus((prev2) => ({
1526
1687
  ...prev2,
1527
1688
  downloaded: /* @__PURE__ */ new Set([...prev2.downloaded, key])
1528
- })), setImportedTranslations((prev2) => /* @__PURE__ */ new Set([...prev2, key])), toast.push({
1689
+ })), setImportedTranslations((prev2) => /* @__PURE__ */ new Set([...prev2, key])), setExistingTranslations(
1690
+ (prev2) => /* @__PURE__ */ new Set([
1691
+ ...prev2,
1692
+ createStableTranslationKey(branchId, documentId, localeId)
1693
+ ])
1694
+ ), toast.push({
1529
1695
  title: `Successfully imported translation for ${documentId} (${localeId})`,
1530
1696
  status: "success",
1531
1697
  closable: !0
@@ -1630,7 +1796,10 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1630
1796
  if (!secrets || documents.length === 0) return 0;
1631
1797
  setIsBusy(!0);
1632
1798
  try {
1633
- const sourceLocale = pluginConfig.getSourceLocale(), publishedDocumentIds = documents.filter((doc) => !doc._id.startsWith("drafts.")).map((doc) => doc._id);
1799
+ const sourceLocale = pluginConfig.getSourceLocale(), sourceDocumentIds = documents.map(getDocumentPublishedId), publishedDocumentIds = await client.fetch(
1800
+ "*[_id in $sourceDocumentIds]._id",
1801
+ { sourceDocumentIds }
1802
+ );
1634
1803
  if (publishedDocumentIds.length === 0)
1635
1804
  return toast.push({
1636
1805
  title: "No published source documents found to publish translations for",
@@ -1649,19 +1818,19 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1649
1818
  }`, {
1650
1819
  sourceLocale,
1651
1820
  publishedDocumentIds
1652
- }), translationDocIds = [];
1821
+ }), translationDocIds = /* @__PURE__ */ new Set();
1653
1822
  if (translationMetadata.forEach((metadata) => {
1654
1823
  metadata.translationDocs?.forEach((translation) => {
1655
- translation.docId && translationDocIds.push(translation.docId);
1824
+ translation.docId && translationDocIds.add(translation.docId);
1656
1825
  });
1657
- }), translationDocIds.length === 0)
1826
+ }), translationDocIds.size === 0)
1658
1827
  return toast.push({
1659
1828
  title: "No translation documents found to publish",
1660
1829
  status: "warning",
1661
1830
  closable: !0
1662
1831
  }), 0;
1663
1832
  const translatedDocumentIds = await publishTranslations(
1664
- translationDocIds,
1833
+ Array.from(translationDocIds),
1665
1834
  client
1666
1835
  );
1667
1836
  return toast.push({
@@ -1686,8 +1855,15 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1686
1855
  }, [fetchLocales, secrets]), o.useEffect(() => {
1687
1856
  documents.length > 0 && locales.length > 0 && fetchExistingTranslations();
1688
1857
  }, [fetchExistingTranslations]), o.useEffect(() => {
1689
- documents.length > 0 && locales.length > 0 && secrets && !loadingDocuments && handleRefreshAll();
1690
- }, [documents]), o.useEffect(() => {
1858
+ documents.length > 0 && locales.length > 0 && secrets && !loadingDocuments && branchId && handleRefreshAll();
1859
+ }, [
1860
+ documents,
1861
+ locales,
1862
+ secrets,
1863
+ loadingDocuments,
1864
+ branchId,
1865
+ handleRefreshAll
1866
+ ]), o.useEffect(() => {
1691
1867
  if (!autoRefresh || documents.length === 0 || !secrets) return;
1692
1868
  const interval = setInterval(async () => {
1693
1869
  await handleRefreshAll();
@@ -1776,7 +1952,7 @@ const LanguageStatus = ({
1776
1952
  importFile,
1777
1953
  isImported = !1
1778
1954
  }) => {
1779
- const [isBusy, setIsBusy] = o.useState(!1), handleImport = o.useCallback(async () => {
1955
+ const [isBusy, setIsBusy] = o.useState(!1), displayedProgress = isImported && progress < 100 ? 100 : progress, handleImport = o.useCallback(async () => {
1780
1956
  setIsBusy(!0);
1781
1957
  try {
1782
1958
  await importFile();
@@ -1786,7 +1962,7 @@ const LanguageStatus = ({
1786
1962
  }, [importFile, setIsBusy]);
1787
1963
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { shadow: 1, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Grid, { columns: 5, gap: 3, padding: 3, children: [
1788
1964
  /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { columnStart: 1, columnEnd: 3, align: "center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "bold", size: 1, children: title }) }),
1789
- typeof progress == "number" ? /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { columnStart: 3, columnEnd: 5, align: "center", children: /* @__PURE__ */ jsxRuntime.jsx(ProgressBar, { progress }) }) : null,
1965
+ typeof displayedProgress == "number" ? /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { columnStart: 3, columnEnd: 5, align: "center", children: /* @__PURE__ */ jsxRuntime.jsx(ProgressBar, { progress: displayedProgress }) }) : null,
1790
1966
  /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { columnStart: 5, columnEnd: 6, children: isImported ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", justify: "center", style: { color: "green" }, children: [
1791
1967
  /* @__PURE__ */ jsxRuntime.jsx(icons.CheckmarkCircleIcon, {}),
1792
1968
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, style: { marginLeft: "4px" }, children: "Imported" })
@@ -3013,12 +3189,17 @@ const WrapText = dt(ui.Box)`
3013
3189
  return locales.filter(
3014
3190
  (locale) => locale.enabled !== !1 && locale.localeId !== sourceLocale
3015
3191
  );
3016
- }, [locales]), documentId = o.useMemo(() => document2 ? document2._id?.replace("drafts.", "") || document2._id : null, [document2]), handleImportTranslations = o.useCallback(
3192
+ }, [locales]), documentId = o.useMemo(() => document2 ? getDocumentPublishedId(document2) : null, [document2]), handleImportTranslations = o.useCallback(
3017
3193
  async (options = {}) => {
3018
3194
  const { autoOnly = !1 } = options;
3019
3195
  if (isImporting || !documentId || autoOnly && !autoImport) return;
3020
3196
  const readyTranslations = availableLocales.filter((locale) => {
3021
- const key = `${branchId}:${documentId}:${document2._rev}:${locale.localeId}`;
3197
+ const key = createTranslationStatusKey(
3198
+ branchId,
3199
+ documentId,
3200
+ document2._rev,
3201
+ locale.localeId
3202
+ );
3022
3203
  return translationStatuses.get(key)?.isReady && !importedTranslations.has(key);
3023
3204
  });
3024
3205
  if (readyTranslations.length !== 0) {
@@ -3139,7 +3320,12 @@ const WrapText = dt(ui.Box)`
3139
3320
  ] })
3140
3321
  ] }),
3141
3322
  /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { children: availableLocales.map((locale) => {
3142
- const key = `${branchId}:${documentId}:${document2._rev}:${locale.localeId}`, status = translationStatuses.get(key), progress = status?.progress || 0, isImported = importedTranslations.has(key);
3323
+ const key = createTranslationStatusKey(
3324
+ branchId,
3325
+ documentId,
3326
+ document2._rev,
3327
+ locale.localeId
3328
+ ), status = translationStatuses.get(key), progress = status?.progress || 0, isImported = importedTranslations.has(key);
3143
3329
  return /* @__PURE__ */ jsxRuntime.jsx(
3144
3330
  LanguageStatus,
3145
3331
  {
@@ -3169,7 +3355,12 @@ const WrapText = dt(ui.Box)`
3169
3355
  text: isImporting ? "Importing..." : "Import All",
3170
3356
  icon: icons.DownloadIcon,
3171
3357
  disabled: isImporting || availableLocales.every((locale) => {
3172
- const key = `${branchId}:${documentId}:${document2._rev}:${locale.localeId}`;
3358
+ const key = createTranslationStatusKey(
3359
+ branchId,
3360
+ documentId,
3361
+ document2._rev,
3362
+ locale.localeId
3363
+ );
3173
3364
  return !translationStatuses.get(key)?.isReady || importedTranslations.has(key);
3174
3365
  }),
3175
3366
  style: { minWidth: "180px" }
@@ -3191,12 +3382,22 @@ const WrapText = dt(ui.Box)`
3191
3382
  "Imported",
3192
3383
  " ",
3193
3384
  availableLocales.filter((locale) => {
3194
- const key = `${branchId}:${documentId}:${document2._rev}:${locale.localeId}`;
3385
+ const key = createTranslationStatusKey(
3386
+ branchId,
3387
+ documentId,
3388
+ document2._rev,
3389
+ locale.localeId
3390
+ );
3195
3391
  return importedTranslations.has(key);
3196
3392
  }).length,
3197
3393
  "/",
3198
3394
  availableLocales.filter((locale) => {
3199
- const key = `${branchId}:${documentId}:${document2._rev}:${locale.localeId}`;
3395
+ const key = createTranslationStatusKey(
3396
+ branchId,
3397
+ documentId,
3398
+ document2._rev,
3399
+ locale.localeId
3400
+ );
3200
3401
  return translationStatuses.get(key)?.isReady;
3201
3402
  }).length
3202
3403
  ] })
@@ -3312,11 +3513,16 @@ const WrapText = dt(ui.Box)`
3312
3513
  } = useTranslations();
3313
3514
  return loadingDocuments ? /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { align: "center", justify: "center", padding: 4, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, {}) }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { style: { maxHeight: "60vh", overflowY: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 2, children: documents.map((document2) => /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { shadow: 1, padding: 3, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
3314
3515
  /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { justify: "space-between", align: "flex-start", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Box, { flex: 1, children: [
3315
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "semibold", size: 1, children: document2._id?.replace("drafts.", "") || document2._id }),
3516
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "semibold", size: 1, children: getDocumentPublishedId(document2) }),
3316
3517
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 0, muted: !0, style: { marginTop: "2px" }, children: document2._type })
3317
3518
  ] }) }),
3318
3519
  /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 2, children: locales.length > 0 ? locales.filter((locale) => locale.enabled !== !1).map((locale) => {
3319
- const documentId = document2._id?.replace("drafts.", "") || document2._id, key = `${branchId}:${documentId}:${document2._rev}:${locale.localeId}`, status = translationStatuses.get(key), isDownloaded = downloadStatus.downloaded.has(key), isImported = importedTranslations.has(key);
3520
+ const documentId = getDocumentPublishedId(document2), key = createTranslationStatusKey(
3521
+ branchId,
3522
+ documentId,
3523
+ document2._rev,
3524
+ locale.localeId
3525
+ ), status = translationStatuses.get(key), isDownloaded = downloadStatus.downloaded.has(key), isImported = importedTranslations.has(key);
3320
3526
  return /* @__PURE__ */ jsxRuntime.jsx(
3321
3527
  LanguageStatus,
3322
3528
  {
@@ -3697,6 +3903,7 @@ const WrapText = dt(ui.Box)`
3697
3903
  singletons,
3698
3904
  singletonMapping,
3699
3905
  ignoreFields,
3906
+ dedupeFields,
3700
3907
  skipFields,
3701
3908
  translateDocuments,
3702
3909
  secretsNamespace = SECRETS_NAMESPACE,
@@ -3717,6 +3924,7 @@ const WrapText = dt(ui.Box)`
3717
3924
  // singletons is a string array of singleton document ids
3718
3925
  singletonMapping || ((sourceDocumentId, locale) => `${sourceDocumentId}-${locale}`),
3719
3926
  ignoreFields || [],
3927
+ dedupeFields || [],
3720
3928
  skipFields || [],
3721
3929
  normalizedTranslateDocuments || [],
3722
3930
  additionalStopTypes,