@braze/sanity-plugin-ai-translation 0.2.72 → 0.2.74
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 +14 -0
- package/dist/index.js +102 -63
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -332,6 +332,20 @@ export declare interface PluginConfig {
|
|
|
332
332
|
* ```
|
|
333
333
|
*/
|
|
334
334
|
excludeFields?: string[];
|
|
335
|
+
/**
|
|
336
|
+
* Fields to completely remove from the translated document.
|
|
337
|
+
* Unlike `excludeFields` (which preserves fields with their original values),
|
|
338
|
+
* `unsetFields` strips matched fields entirely so they are absent from the output.
|
|
339
|
+
* Supports the same pattern syntax as `excludeFields`: field names (e.g., 'courseId'),
|
|
340
|
+
* nested paths (e.g., 'meta.internalId'), wildcard arrays (e.g., 'sections[*].legacyId'),
|
|
341
|
+
* and hierarchical matches (e.g., '**.tmpField').
|
|
342
|
+
*
|
|
343
|
+
* @example
|
|
344
|
+
* ```typescript
|
|
345
|
+
* unsetFields: ['courseId', 'meta.internalId', 'sections[*].legacyId']
|
|
346
|
+
* ```
|
|
347
|
+
*/
|
|
348
|
+
unsetFields?: string[];
|
|
335
349
|
/**
|
|
336
350
|
* Optional telemetry configuration for monitoring translation operations.
|
|
337
351
|
* Provides callbacks for lightweight events and detailed error bundles.
|
package/dist/index.js
CHANGED
|
@@ -482,8 +482,8 @@ function isNetworkError(error) {
|
|
|
482
482
|
return response.status >= 500 || response.status === 429;
|
|
483
483
|
}
|
|
484
484
|
if (error instanceof Error) {
|
|
485
|
-
const message = error.message;
|
|
486
|
-
if (/\b(50[0-9]|429)\b/.test(message))
|
|
485
|
+
const message = error.message.toLowerCase();
|
|
486
|
+
if (/\b(50[0-9]|429)\b/.test(message) || message.includes("rate limit"))
|
|
487
487
|
return !0;
|
|
488
488
|
}
|
|
489
489
|
return !1;
|
|
@@ -870,7 +870,14 @@ const fetchTranslationPrompts = async ({
|
|
|
870
870
|
languageFieldName
|
|
871
871
|
}) => {
|
|
872
872
|
try {
|
|
873
|
-
const doc = await
|
|
873
|
+
const doc = await retryWithBackoff(
|
|
874
|
+
() => client.fetch("*[_id == $documentId][0]", { documentId }),
|
|
875
|
+
{
|
|
876
|
+
maxAttempts: 3,
|
|
877
|
+
initialDelayMs: 1e3,
|
|
878
|
+
context: { operation: "localizeReferences.fetchDocument", documentId }
|
|
879
|
+
}
|
|
880
|
+
);
|
|
874
881
|
if (!doc) {
|
|
875
882
|
devLog.error("Document not found", new Error("Document not found"), {
|
|
876
883
|
documentId,
|
|
@@ -900,17 +907,24 @@ const fetchTranslationPrompts = async ({
|
|
|
900
907
|
}, updateReferences = async (documentId, client, refs, sourceLanguage, targetLanguage) => {
|
|
901
908
|
if (!refs?.length)
|
|
902
909
|
return [];
|
|
903
|
-
const refIds = refs.map((r) => getPublishedId(r.value)), translations = await
|
|
904
|
-
|
|
910
|
+
const refIds = refs.map((r) => getPublishedId(r.value)), translations = await retryWithBackoff(
|
|
911
|
+
() => client.fetch(
|
|
912
|
+
`*[_type == $translationMetadataSchemaType && array::intersects(translations[].value._ref, $refIds)] {
|
|
905
913
|
"sourceId": translations[_key == $sourceLanguage][0].value._ref,
|
|
906
914
|
"targetId": translations[_key == $targetLanguage][0].value._ref,
|
|
907
915
|
"targetType": translations[_key == $sourceLanguage][0].value -> _type
|
|
908
916
|
}`,
|
|
917
|
+
{
|
|
918
|
+
translationMetadataSchemaType: TRANSLATION_METADATA_TYPE,
|
|
919
|
+
refIds,
|
|
920
|
+
sourceLanguage,
|
|
921
|
+
targetLanguage
|
|
922
|
+
}
|
|
923
|
+
),
|
|
909
924
|
{
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
targetLanguage
|
|
925
|
+
maxAttempts: 3,
|
|
926
|
+
initialDelayMs: 1e3,
|
|
927
|
+
context: { operation: "localizeReferences.fetchTranslations", refCount: refIds.length }
|
|
914
928
|
}
|
|
915
929
|
), translationMap = /* @__PURE__ */ new Map();
|
|
916
930
|
for (const t of translations)
|
|
@@ -948,81 +962,80 @@ const fetchTranslationPrompts = async ({
|
|
|
948
962
|
}, updateBackwardReferences = async ({
|
|
949
963
|
client,
|
|
950
964
|
releaseId,
|
|
951
|
-
sourceDocumentIds,
|
|
952
965
|
targetLanguages,
|
|
953
966
|
sourceLanguage,
|
|
954
967
|
languageFieldName,
|
|
955
968
|
excludeDocumentIds
|
|
956
969
|
}) => {
|
|
957
|
-
if (
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
});
|
|
967
|
-
try {
|
|
968
|
-
for (const targetLanguage of targetLanguages) {
|
|
969
|
-
const existingDocsWithStaleRefs = await client.fetch(
|
|
970
|
+
if (targetLanguages.length) {
|
|
971
|
+
devLog.debug("Starting backward reference update", {
|
|
972
|
+
releaseId,
|
|
973
|
+
targetLanguages,
|
|
974
|
+
excludeCount: excludeDocumentIds.length,
|
|
975
|
+
component: "updateBackwardReferences"
|
|
976
|
+
});
|
|
977
|
+
try {
|
|
978
|
+
const existingDocs = await client.fetch(
|
|
970
979
|
`*[
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
^references(@._id)
|
|
979
|
-
]) > 0
|
|
980
|
-
}[hasStaleRefs == true]`,
|
|
980
|
+
sanity::partOfRelease($releaseId) &&
|
|
981
|
+
@[$languageFieldName] in $targetLanguages &&
|
|
982
|
+
!(_id in $excludeIds)
|
|
983
|
+
]{
|
|
984
|
+
_id,
|
|
985
|
+
"language": @[$languageFieldName]
|
|
986
|
+
}`,
|
|
981
987
|
{
|
|
982
988
|
releaseId,
|
|
983
|
-
|
|
989
|
+
targetLanguages,
|
|
984
990
|
languageFieldName,
|
|
985
|
-
excludeIds: excludeDocumentIds
|
|
986
|
-
sourceDocumentIds: normalizedSourceIds
|
|
991
|
+
excludeIds: excludeDocumentIds
|
|
987
992
|
}
|
|
988
993
|
);
|
|
989
|
-
if (
|
|
990
|
-
devLog.debug("No existing documents
|
|
994
|
+
if (existingDocs.length === 0) {
|
|
995
|
+
devLog.debug("No existing documents found in release for target languages", {
|
|
991
996
|
releaseId,
|
|
992
|
-
|
|
997
|
+
targetLanguages,
|
|
993
998
|
component: "updateBackwardReferences"
|
|
994
999
|
});
|
|
995
|
-
|
|
1000
|
+
return;
|
|
996
1001
|
}
|
|
997
|
-
devLog.info("
|
|
1002
|
+
devLog.info("Processing existing documents for backward reference updates", {
|
|
998
1003
|
releaseId,
|
|
999
|
-
|
|
1000
|
-
count:
|
|
1001
|
-
documentIds: existingDocsWithStaleRefs.map((d) => d._id),
|
|
1004
|
+
targetLanguages,
|
|
1005
|
+
count: existingDocs.length,
|
|
1002
1006
|
component: "updateBackwardReferences"
|
|
1003
|
-
})
|
|
1004
|
-
|
|
1007
|
+
});
|
|
1008
|
+
let updated = 0, errors = 0;
|
|
1009
|
+
for (const { _id } of existingDocs)
|
|
1010
|
+
try {
|
|
1005
1011
|
await localizeReferences({
|
|
1006
1012
|
client,
|
|
1007
1013
|
documentId: _id,
|
|
1008
1014
|
sourceLanguage,
|
|
1009
1015
|
languageFieldName
|
|
1016
|
+
}), updated++;
|
|
1017
|
+
} catch (error) {
|
|
1018
|
+
errors++, devLog.warn("Failed to update references for document", {
|
|
1019
|
+
documentId: _id,
|
|
1020
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1021
|
+
component: "updateBackwardReferences"
|
|
1010
1022
|
});
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1023
|
+
}
|
|
1024
|
+
devLog.info("Backward reference update complete", {
|
|
1013
1025
|
releaseId,
|
|
1014
|
-
|
|
1015
|
-
|
|
1026
|
+
targetLanguages,
|
|
1027
|
+
processed: existingDocs.length,
|
|
1028
|
+
updated,
|
|
1029
|
+
errors,
|
|
1030
|
+
component: "updateBackwardReferences"
|
|
1031
|
+
});
|
|
1032
|
+
} catch (error) {
|
|
1033
|
+
devLog.error("Error updating backward references", error instanceof Error ? error : new Error(String(error)), {
|
|
1034
|
+
releaseId,
|
|
1035
|
+
targetLanguages,
|
|
1016
1036
|
component: "updateBackwardReferences"
|
|
1017
1037
|
});
|
|
1018
1038
|
}
|
|
1019
|
-
} catch (error) {
|
|
1020
|
-
devLog.error("Error updating backward references", error instanceof Error ? error : new Error(String(error)), {
|
|
1021
|
-
releaseId,
|
|
1022
|
-
sourceDocumentIds: normalizedSourceIds,
|
|
1023
|
-
targetLanguages,
|
|
1024
|
-
component: "updateBackwardReferences"
|
|
1025
|
-
});
|
|
1026
1039
|
}
|
|
1027
1040
|
}, setupTranslationJobStatuses = (dispatch, sourceDocuments, targetLanguages, mainSourceDocumentId) => {
|
|
1028
1041
|
for (const sourceDocument of sourceDocuments)
|
|
@@ -1347,6 +1360,16 @@ const COMMON_ENUM_FIELDS = [
|
|
|
1347
1360
|
/** SEO and URL-related fields that often shouldn't be translated */
|
|
1348
1361
|
seo: ["slug", "handle", "permalink", "canonical", "redirect", "robots", "sitemap"]
|
|
1349
1362
|
};
|
|
1363
|
+
function unsetDocumentFields(document, unsetFields) {
|
|
1364
|
+
if (!unsetFields.length)
|
|
1365
|
+
return document;
|
|
1366
|
+
const { filteredDocument, excludedValues } = filterExcludedFields(document, unsetFields);
|
|
1367
|
+
return Object.keys(excludedValues).length > 0 && devLog.info("Unset fields from document (permanently removed)", {
|
|
1368
|
+
documentId: document._id || "unknown",
|
|
1369
|
+
unsetFieldCount: Object.keys(excludedValues).length,
|
|
1370
|
+
unsetFields: Object.keys(excludedValues)
|
|
1371
|
+
}), filteredDocument;
|
|
1372
|
+
}
|
|
1350
1373
|
function applyTransforms({
|
|
1351
1374
|
document,
|
|
1352
1375
|
registry,
|
|
@@ -2617,11 +2640,17 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2617
2640
|
openaiConfig,
|
|
2618
2641
|
releaseId,
|
|
2619
2642
|
excludeFields = [],
|
|
2643
|
+
unsetFields = [],
|
|
2620
2644
|
replaceExisting = !1,
|
|
2621
2645
|
mainSourceDocumentId,
|
|
2622
2646
|
telemetryConfig
|
|
2623
2647
|
}) => {
|
|
2624
|
-
const startTime = Date.now(), sourceLanguage = sourceDocument[languageFieldName],
|
|
2648
|
+
const startTime = Date.now(), sourceLanguage = sourceDocument[languageFieldName], safeUnsetFields = unsetFields.filter((field) => field !== languageFieldName);
|
|
2649
|
+
unsetFields.length !== safeUnsetFields.length && devLog.warn("Language field was in unsetFields - removing it to prevent translation errors", {
|
|
2650
|
+
documentId: sourceDocument._id,
|
|
2651
|
+
languageFieldName
|
|
2652
|
+
});
|
|
2653
|
+
const documentAfterUnset = unsetDocumentFields(sourceDocument, safeUnsetFields), safeExcludeFields = excludeFields.filter((field) => field !== languageFieldName);
|
|
2625
2654
|
excludeFields.length !== safeExcludeFields.length && devLog.warn("Language field was in excludeFields - removing it to prevent translation errors", {
|
|
2626
2655
|
documentId: sourceDocument._id,
|
|
2627
2656
|
languageFieldName,
|
|
@@ -2632,10 +2661,10 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2632
2661
|
documentType: sourceDocument._type,
|
|
2633
2662
|
excludeFields: safeExcludeFields,
|
|
2634
2663
|
excludeFieldsLength: safeExcludeFields?.length ?? 0,
|
|
2635
|
-
sourceHasTitle: "title" in
|
|
2636
|
-
sourceTitleValue:
|
|
2664
|
+
sourceHasTitle: "title" in documentAfterUnset,
|
|
2665
|
+
sourceTitleValue: documentAfterUnset.title
|
|
2637
2666
|
});
|
|
2638
|
-
const { filteredDocument, excludedValues } = filterExcludedFields(
|
|
2667
|
+
const { filteredDocument, excludedValues } = filterExcludedFields(documentAfterUnset, safeExcludeFields);
|
|
2639
2668
|
devLog.info("After filtering excluded fields", {
|
|
2640
2669
|
documentId: sourceDocument._id,
|
|
2641
2670
|
filteredHasTitle: "title" in filteredDocument,
|
|
@@ -2858,6 +2887,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2858
2887
|
openaiConfig,
|
|
2859
2888
|
releaseId,
|
|
2860
2889
|
excludeFields,
|
|
2890
|
+
unsetFields,
|
|
2861
2891
|
replaceExisting,
|
|
2862
2892
|
mainSourceDocumentId,
|
|
2863
2893
|
telemetryConfig
|
|
@@ -2887,6 +2917,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2887
2917
|
openaiConfig,
|
|
2888
2918
|
releaseId,
|
|
2889
2919
|
excludeFields,
|
|
2920
|
+
unsetFields,
|
|
2890
2921
|
replaceExisting,
|
|
2891
2922
|
mainSourceDocumentId,
|
|
2892
2923
|
telemetryConfig
|
|
@@ -3002,6 +3033,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3002
3033
|
translationMode,
|
|
3003
3034
|
openaiConfig,
|
|
3004
3035
|
excludeFields,
|
|
3036
|
+
unsetFields,
|
|
3005
3037
|
releaseConfig,
|
|
3006
3038
|
telemetryConfig
|
|
3007
3039
|
}) => {
|
|
@@ -3122,6 +3154,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3122
3154
|
openaiConfig,
|
|
3123
3155
|
releaseId,
|
|
3124
3156
|
excludeFields,
|
|
3157
|
+
unsetFields,
|
|
3125
3158
|
replaceExisting: releaseConfig.replaceExisting,
|
|
3126
3159
|
// Pass main source document ID for active translations tracking
|
|
3127
3160
|
mainSourceDocumentId: sourceDocument._id,
|
|
@@ -3176,10 +3209,14 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3176
3209
|
languageFieldName
|
|
3177
3210
|
});
|
|
3178
3211
|
})
|
|
3179
|
-
),
|
|
3212
|
+
), devLog.debug("Checking if backward reference update is needed", {
|
|
3213
|
+
releaseMode: releaseConfig.mode,
|
|
3214
|
+
releaseId,
|
|
3215
|
+
willUpdate: releaseConfig.mode === "existing" && !!releaseId,
|
|
3216
|
+
component: "translateDocumentSet"
|
|
3217
|
+
}), releaseConfig.mode === "existing" && releaseId && await updateBackwardReferences({
|
|
3180
3218
|
client,
|
|
3181
3219
|
releaseId,
|
|
3182
|
-
sourceDocumentIds,
|
|
3183
3220
|
targetLanguages,
|
|
3184
3221
|
sourceLanguage,
|
|
3185
3222
|
languageFieldName,
|
|
@@ -3302,6 +3339,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3302
3339
|
translationMode,
|
|
3303
3340
|
openaiConfig: config.openai,
|
|
3304
3341
|
excludeFields: config.excludeFields,
|
|
3342
|
+
unsetFields: config.unsetFields,
|
|
3305
3343
|
releaseConfig: {
|
|
3306
3344
|
...releaseConfig,
|
|
3307
3345
|
replaceExisting,
|
|
@@ -3324,6 +3362,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3324
3362
|
config.maxReferenceDepth,
|
|
3325
3363
|
config.openai,
|
|
3326
3364
|
config.excludeFields,
|
|
3365
|
+
config.unsetFields,
|
|
3327
3366
|
config.telemetry,
|
|
3328
3367
|
filteredReferences,
|
|
3329
3368
|
translationMode,
|