@braze/sanity-plugin-ai-translation 0.2.74 → 0.2.76
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.
- package/dist/index.d.ts +30 -2
- package/dist/index.js +58 -24
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -340,12 +340,18 @@ export declare interface PluginConfig {
|
|
|
340
340
|
* nested paths (e.g., 'meta.internalId'), wildcard arrays (e.g., 'sections[*].legacyId'),
|
|
341
341
|
* and hierarchical matches (e.g., '**.tmpField').
|
|
342
342
|
*
|
|
343
|
+
* Each entry can be a plain string (always unset) or an object with `preserveExisting: true`
|
|
344
|
+
* to retain the field's value from the published target document during re-translation.
|
|
345
|
+
*
|
|
343
346
|
* @example
|
|
344
347
|
* ```typescript
|
|
345
|
-
* unsetFields: [
|
|
348
|
+
* unsetFields: [
|
|
349
|
+
* 'legacyId',
|
|
350
|
+
* { field: 'skilljarCourseId', preserveExisting: true }
|
|
351
|
+
* ]
|
|
346
352
|
* ```
|
|
347
353
|
*/
|
|
348
|
-
unsetFields?:
|
|
354
|
+
unsetFields?: UnsetFieldEntry[];
|
|
349
355
|
/**
|
|
350
356
|
* Optional telemetry configuration for monitoring translation operations.
|
|
351
357
|
* Provides callbacks for lightweight events and detailed error bundles.
|
|
@@ -717,6 +723,28 @@ declare interface TranslationJobFailedEvent extends TelemetryEventBase {
|
|
|
717
723
|
|
|
718
724
|
declare type TranslationMode = "sanity-only" | "openai-only";
|
|
719
725
|
|
|
726
|
+
/**
|
|
727
|
+
* Configuration for a field that should be unset with additional options.
|
|
728
|
+
* @public
|
|
729
|
+
*/
|
|
730
|
+
declare interface UnsetFieldConfig {
|
|
731
|
+
/** Field pattern to unset (same syntax as `excludeFields`) */
|
|
732
|
+
field: string;
|
|
733
|
+
/**
|
|
734
|
+
* When true, preserves the field's value from the published target document
|
|
735
|
+
* during re-translation. Has no effect on first translation (no existing target).
|
|
736
|
+
* Defaults to false.
|
|
737
|
+
*/
|
|
738
|
+
preserveExisting?: boolean;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* An entry in the `unsetFields` array: either a plain field pattern string
|
|
743
|
+
* or an object with additional configuration.
|
|
744
|
+
* @public
|
|
745
|
+
*/
|
|
746
|
+
declare type UnsetFieldEntry = string | UnsetFieldConfig;
|
|
747
|
+
|
|
720
748
|
/**
|
|
721
749
|
* Represents a VTT caption cue with timing and text content.
|
|
722
750
|
*/
|
package/dist/index.js
CHANGED
|
@@ -870,14 +870,11 @@ const fetchTranslationPrompts = async ({
|
|
|
870
870
|
languageFieldName
|
|
871
871
|
}) => {
|
|
872
872
|
try {
|
|
873
|
-
const doc = await retryWithBackoff(
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
context: { operation: "localizeReferences.fetchDocument", documentId }
|
|
879
|
-
}
|
|
880
|
-
);
|
|
873
|
+
const doc = await retryWithBackoff(() => client.fetch("*[_id == $documentId][0]", { documentId }), {
|
|
874
|
+
maxAttempts: 3,
|
|
875
|
+
initialDelayMs: 1e3,
|
|
876
|
+
context: { operation: "localizeReferences.fetchDocument", documentId }
|
|
877
|
+
});
|
|
881
878
|
if (!doc) {
|
|
882
879
|
devLog.error("Document not found", new Error("Document not found"), {
|
|
883
880
|
documentId,
|
|
@@ -904,20 +901,19 @@ const fetchTranslationPrompts = async ({
|
|
|
904
901
|
component: "localizeReferences"
|
|
905
902
|
});
|
|
906
903
|
}
|
|
907
|
-
}, updateReferences = async (documentId, client, refs,
|
|
904
|
+
}, updateReferences = async (documentId, client, refs, _sourceLanguage, targetLanguage) => {
|
|
908
905
|
if (!refs?.length)
|
|
909
906
|
return [];
|
|
910
907
|
const refIds = refs.map((r) => getPublishedId(r.value)), translations = await retryWithBackoff(
|
|
911
908
|
() => client.fetch(
|
|
912
909
|
`*[_type == $translationMetadataSchemaType && array::intersects(translations[].value._ref, $refIds)] {
|
|
913
|
-
"
|
|
910
|
+
"allSourceRefs": translations[_key != $targetLanguage][].value._ref,
|
|
914
911
|
"targetId": translations[_key == $targetLanguage][0].value._ref,
|
|
915
|
-
"targetType": translations[_key == $
|
|
912
|
+
"targetType": translations[_key == $targetLanguage][0].value -> _type
|
|
916
913
|
}`,
|
|
917
914
|
{
|
|
918
915
|
translationMetadataSchemaType: TRANSLATION_METADATA_TYPE,
|
|
919
916
|
refIds,
|
|
920
|
-
sourceLanguage,
|
|
921
917
|
targetLanguage
|
|
922
918
|
}
|
|
923
919
|
),
|
|
@@ -928,16 +924,21 @@ const fetchTranslationPrompts = async ({
|
|
|
928
924
|
}
|
|
929
925
|
), translationMap = /* @__PURE__ */ new Map();
|
|
930
926
|
for (const t of translations)
|
|
931
|
-
|
|
927
|
+
if (t.targetId)
|
|
928
|
+
for (const sourceRef of t.allSourceRefs ?? [])
|
|
929
|
+
sourceRef && translationMap.set(sourceRef, { targetId: t.targetId, targetType: t.targetType });
|
|
932
930
|
const patch = client.patch(documentId);
|
|
933
931
|
let hasChanges = !1;
|
|
934
932
|
for (const ref of refs) {
|
|
935
|
-
const sourceId = getPublishedId(ref.value),
|
|
936
|
-
|
|
933
|
+
const sourceId = getPublishedId(ref.value), match = translationMap.get(sourceId);
|
|
934
|
+
if (!match)
|
|
935
|
+
continue;
|
|
936
|
+
const newId = getPublishedId(match.targetId);
|
|
937
|
+
patch.set({ [pathToString(ref.path)]: newId }), patch.set({
|
|
937
938
|
[`${pathToString(ref.path.slice(0, -1))}._strengthenOnPublish`]: {
|
|
938
|
-
type:
|
|
939
|
+
type: match.targetType || void 0
|
|
939
940
|
}
|
|
940
|
-
}), hasChanges = !0
|
|
941
|
+
}), hasChanges = !0;
|
|
941
942
|
}
|
|
942
943
|
hasChanges && (await retryWithBackoff(
|
|
943
944
|
async () => {
|
|
@@ -1360,10 +1361,16 @@ const COMMON_ENUM_FIELDS = [
|
|
|
1360
1361
|
/** SEO and URL-related fields that often shouldn't be translated */
|
|
1361
1362
|
seo: ["slug", "handle", "permalink", "canonical", "redirect", "robots", "sitemap"]
|
|
1362
1363
|
};
|
|
1364
|
+
function normalizeUnsetFields(entries) {
|
|
1365
|
+
const allFieldPatterns = [], preserveFieldPatterns = [];
|
|
1366
|
+
for (const entry of entries)
|
|
1367
|
+
typeof entry == "string" ? allFieldPatterns.push(entry) : (allFieldPatterns.push(entry.field), entry.preserveExisting && preserveFieldPatterns.push(entry.field));
|
|
1368
|
+
return { allFieldPatterns, preserveFieldPatterns };
|
|
1369
|
+
}
|
|
1363
1370
|
function unsetDocumentFields(document, unsetFields) {
|
|
1364
1371
|
if (!unsetFields.length)
|
|
1365
1372
|
return document;
|
|
1366
|
-
const { filteredDocument, excludedValues } = filterExcludedFields(document,
|
|
1373
|
+
const { allFieldPatterns } = normalizeUnsetFields(unsetFields), { filteredDocument, excludedValues } = filterExcludedFields(document, allFieldPatterns);
|
|
1367
1374
|
return Object.keys(excludedValues).length > 0 && devLog.info("Unset fields from document (permanently removed)", {
|
|
1368
1375
|
documentId: document._id || "unknown",
|
|
1369
1376
|
unsetFieldCount: Object.keys(excludedValues).length,
|
|
@@ -2645,12 +2652,39 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2645
2652
|
mainSourceDocumentId,
|
|
2646
2653
|
telemetryConfig
|
|
2647
2654
|
}) => {
|
|
2648
|
-
const startTime = Date.now(), sourceLanguage = sourceDocument[languageFieldName], safeUnsetFields = unsetFields.filter((
|
|
2655
|
+
const startTime = Date.now(), sourceLanguage = sourceDocument[languageFieldName], safeUnsetFields = unsetFields.filter((entry) => (typeof entry == "string" ? entry : entry.field) !== languageFieldName);
|
|
2649
2656
|
unsetFields.length !== safeUnsetFields.length && devLog.warn("Language field was in unsetFields - removing it to prevent translation errors", {
|
|
2650
2657
|
documentId: sourceDocument._id,
|
|
2651
2658
|
languageFieldName
|
|
2652
2659
|
});
|
|
2653
|
-
const documentAfterUnset = unsetDocumentFields(sourceDocument, safeUnsetFields),
|
|
2660
|
+
const documentAfterUnset = unsetDocumentFields(sourceDocument, safeUnsetFields), { preserveFieldPatterns } = normalizeUnsetFields(safeUnsetFields);
|
|
2661
|
+
let documentWithPreserved = documentAfterUnset;
|
|
2662
|
+
if (preserveFieldPatterns.length > 0 && existingTranslationId)
|
|
2663
|
+
try {
|
|
2664
|
+
const publishedTarget = await client.getDocument(existingTranslationId);
|
|
2665
|
+
if (publishedTarget) {
|
|
2666
|
+
const { excludedValues: preserveValues } = filterExcludedFields(
|
|
2667
|
+
publishedTarget,
|
|
2668
|
+
preserveFieldPatterns
|
|
2669
|
+
);
|
|
2670
|
+
Object.keys(preserveValues).length > 0 && (documentWithPreserved = restoreExcludedFields(documentAfterUnset, preserveValues), devLog.info("Preserved fields from published target during re-translation", {
|
|
2671
|
+
documentId: sourceDocument._id,
|
|
2672
|
+
existingTranslationId,
|
|
2673
|
+
preservedFields: Object.keys(preserveValues)
|
|
2674
|
+
}));
|
|
2675
|
+
} else
|
|
2676
|
+
devLog.debug("Published target not found for preserveExisting fields", {
|
|
2677
|
+
documentId: sourceDocument._id,
|
|
2678
|
+
existingTranslationId
|
|
2679
|
+
});
|
|
2680
|
+
} catch (error) {
|
|
2681
|
+
devLog.warn("Failed to fetch published target for preserveExisting fields", {
|
|
2682
|
+
documentId: sourceDocument._id,
|
|
2683
|
+
existingTranslationId,
|
|
2684
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2685
|
+
});
|
|
2686
|
+
}
|
|
2687
|
+
const safeExcludeFields = excludeFields.filter((field) => field !== languageFieldName);
|
|
2654
2688
|
excludeFields.length !== safeExcludeFields.length && devLog.warn("Language field was in excludeFields - removing it to prevent translation errors", {
|
|
2655
2689
|
documentId: sourceDocument._id,
|
|
2656
2690
|
languageFieldName,
|
|
@@ -2661,10 +2695,10 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2661
2695
|
documentType: sourceDocument._type,
|
|
2662
2696
|
excludeFields: safeExcludeFields,
|
|
2663
2697
|
excludeFieldsLength: safeExcludeFields?.length ?? 0,
|
|
2664
|
-
sourceHasTitle: "title" in
|
|
2665
|
-
sourceTitleValue:
|
|
2698
|
+
sourceHasTitle: "title" in documentWithPreserved,
|
|
2699
|
+
sourceTitleValue: documentWithPreserved.title
|
|
2666
2700
|
});
|
|
2667
|
-
const { filteredDocument, excludedValues } = filterExcludedFields(
|
|
2701
|
+
const { filteredDocument, excludedValues } = filterExcludedFields(documentWithPreserved, safeExcludeFields);
|
|
2668
2702
|
devLog.info("After filtering excluded fields", {
|
|
2669
2703
|
documentId: sourceDocument._id,
|
|
2670
2704
|
filteredHasTitle: "title" in filteredDocument,
|
|
@@ -3078,7 +3112,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3078
3112
|
metadata: {
|
|
3079
3113
|
title: releaseTitle,
|
|
3080
3114
|
description: releaseDescription,
|
|
3081
|
-
releaseType: "
|
|
3115
|
+
releaseType: "asap"
|
|
3082
3116
|
}
|
|
3083
3117
|
}),
|
|
3084
3118
|
{
|