@braze/sanity-plugin-ai-translation 0.2.51 → 0.2.52
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 +51 -0
- package/dist/index.js +276 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -446,6 +446,21 @@ declare interface TranslatableVTTCaptions {
|
|
|
446
446
|
_originalMetadata: VTTCaptionMetadata[];
|
|
447
447
|
}
|
|
448
448
|
|
|
449
|
+
/**
|
|
450
|
+
* Special type to track transformed Wistia captions.
|
|
451
|
+
* This combines metadata separation for token efficiency with compact text format for translation.
|
|
452
|
+
*/
|
|
453
|
+
declare interface TranslatableWistiaCaptions {
|
|
454
|
+
_type: "translatable.wistia.captions";
|
|
455
|
+
translationText: string;
|
|
456
|
+
_originalMetadata: WistiaCaptionsMetadata;
|
|
457
|
+
_srtCues: Array<{
|
|
458
|
+
index: number;
|
|
459
|
+
startTime: string;
|
|
460
|
+
endTime: string;
|
|
461
|
+
}>;
|
|
462
|
+
}
|
|
463
|
+
|
|
449
464
|
/**
|
|
450
465
|
* Schema for translation history
|
|
451
466
|
*
|
|
@@ -497,4 +512,40 @@ export declare const vttCaptionTransform: FieldTransform<
|
|
|
497
512
|
TranslatableVTTCaptions
|
|
498
513
|
>;
|
|
499
514
|
|
|
515
|
+
/**
|
|
516
|
+
* Represents a Wistia captions object with SRT content and metadata.
|
|
517
|
+
*/
|
|
518
|
+
declare interface WistiaCaptions {
|
|
519
|
+
captionContent?: string;
|
|
520
|
+
captionFileName?: string;
|
|
521
|
+
isoLanguage?: string;
|
|
522
|
+
captionUploadStatus?: string;
|
|
523
|
+
captionUploadMessage?: string;
|
|
524
|
+
[key: string]: string | undefined;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Wistia captions metadata without captionContent, used for preservation during translation.
|
|
529
|
+
*/
|
|
530
|
+
declare interface WistiaCaptionsMetadata {
|
|
531
|
+
captionFileName?: string;
|
|
532
|
+
isoLanguage?: string;
|
|
533
|
+
captionUploadStatus?: string;
|
|
534
|
+
captionUploadMessage?: string;
|
|
535
|
+
_originalIndex: number;
|
|
536
|
+
[key: string]: string | number | undefined;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Transform for handling Wistia captions in documents using SRT format.
|
|
541
|
+
* This stores metadata separately for token efficiency while formatting
|
|
542
|
+
* text content in a compact indexed format for better translation results.
|
|
543
|
+
*
|
|
544
|
+
* @public
|
|
545
|
+
*/
|
|
546
|
+
export declare const wistiaCaptionTransform: FieldTransform<
|
|
547
|
+
WistiaCaptions,
|
|
548
|
+
TranslatableWistiaCaptions
|
|
549
|
+
>;
|
|
550
|
+
|
|
500
551
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -80,7 +80,9 @@ const isLoggingEnabled = () => process.env.NEXT_PUBLIC_SANITY_STUDIO_AI_TRANSLAT
|
|
|
80
80
|
}, updateActiveTranslationJob = (documentId, job) => {
|
|
81
81
|
const entry = activeTranslations.get(documentId);
|
|
82
82
|
if (entry) {
|
|
83
|
-
const jobKey = `${job._id}_${job.language}`, existingJob = entry.state.translationJobs.get(jobKey),
|
|
83
|
+
const jobKey = `${job._id}_${job.language}`, existingJob = entry.state.translationJobs.get(jobKey), jobWithDefinedValues = Object.fromEntries(
|
|
84
|
+
Object.entries(job).filter(([_, value]) => value !== void 0)
|
|
85
|
+
), mergedJob = existingJob ? { ...existingJob, ...jobWithDefinedValues } : jobWithDefinedValues;
|
|
84
86
|
entry.state.translationJobs.set(jobKey, mergedJob), notifySubscribers(entry);
|
|
85
87
|
}
|
|
86
88
|
}, getActiveTranslation = (documentId) => activeTranslations.get(documentId)?.state ?? null, subscribeToActiveTranslation = (documentId, callback) => {
|
|
@@ -3799,6 +3801,277 @@ function deserializeVTTCaptionText(translationText, originalMetadata) {
|
|
|
3799
3801
|
resultExample: result.length > 0 ? result[0] : null
|
|
3800
3802
|
}), result;
|
|
3801
3803
|
}
|
|
3804
|
+
const LOCALE_TO_ISO = {
|
|
3805
|
+
// English variants
|
|
3806
|
+
"en-us": "eng",
|
|
3807
|
+
"en-US": "eng",
|
|
3808
|
+
"en-gb": "eng",
|
|
3809
|
+
"en-GB": "eng",
|
|
3810
|
+
"en-ca": "eng",
|
|
3811
|
+
"en-CA": "eng",
|
|
3812
|
+
"en-au": "eng",
|
|
3813
|
+
"en-AU": "eng",
|
|
3814
|
+
// Spanish variants
|
|
3815
|
+
es: "spa",
|
|
3816
|
+
"es-es": "spa",
|
|
3817
|
+
"es-ES": "spa",
|
|
3818
|
+
"es-mx": "spa",
|
|
3819
|
+
"es-MX": "spa",
|
|
3820
|
+
// French variants
|
|
3821
|
+
fr: "fra",
|
|
3822
|
+
"fr-fr": "fra",
|
|
3823
|
+
"fr-FR": "fra",
|
|
3824
|
+
"fr-ca": "fra",
|
|
3825
|
+
"fr-CA": "fra",
|
|
3826
|
+
// German variants
|
|
3827
|
+
de: "ger",
|
|
3828
|
+
"de-de": "ger",
|
|
3829
|
+
"de-DE": "ger",
|
|
3830
|
+
"de-at": "ger",
|
|
3831
|
+
"de-AT": "ger",
|
|
3832
|
+
// Italian variants
|
|
3833
|
+
it: "ita",
|
|
3834
|
+
"it-it": "ita",
|
|
3835
|
+
"it-IT": "ita",
|
|
3836
|
+
// Portuguese variants
|
|
3837
|
+
pt: "por",
|
|
3838
|
+
"pt-br": "por",
|
|
3839
|
+
"pt-BR": "por",
|
|
3840
|
+
"pt-pt": "por",
|
|
3841
|
+
"pt-PT": "por",
|
|
3842
|
+
// Russian variants
|
|
3843
|
+
ru: "rus",
|
|
3844
|
+
"ru-ru": "rus",
|
|
3845
|
+
"ru-RU": "rus",
|
|
3846
|
+
// Japanese variants
|
|
3847
|
+
ja: "jpn",
|
|
3848
|
+
"ja-jp": "jpn",
|
|
3849
|
+
"ja-JP": "jpn",
|
|
3850
|
+
// Korean variants
|
|
3851
|
+
ko: "kor",
|
|
3852
|
+
"ko-kr": "kor",
|
|
3853
|
+
"ko-KR": "kor",
|
|
3854
|
+
// Chinese variants
|
|
3855
|
+
zh: "chi",
|
|
3856
|
+
"zh-cn": "chi",
|
|
3857
|
+
"zh-CN": "chi",
|
|
3858
|
+
"zh-tw": "chi",
|
|
3859
|
+
"zh-TW": "chi",
|
|
3860
|
+
"zh-hk": "chi",
|
|
3861
|
+
"zh-HK": "chi"
|
|
3862
|
+
}, normalizeToIsoLanguageCode = (code) => {
|
|
3863
|
+
const normalizedCode = code.toLowerCase();
|
|
3864
|
+
return LOCALE_TO_ISO[normalizedCode];
|
|
3865
|
+
}, wistiaCaptionTransform = {
|
|
3866
|
+
matcher: {
|
|
3867
|
+
fieldName: "wistiaCaptions"
|
|
3868
|
+
},
|
|
3869
|
+
toTranslatable: (wistiaCaptions, context) => {
|
|
3870
|
+
if (devLog.info("Wistia captions toTranslatable - input:", {
|
|
3871
|
+
fieldPath: context.fieldPath,
|
|
3872
|
+
hasCaptionContent: !!wistiaCaptions?.captionContent,
|
|
3873
|
+
captionContentLength: wistiaCaptions?.captionContent?.length ?? 0,
|
|
3874
|
+
isoLanguage: wistiaCaptions?.isoLanguage
|
|
3875
|
+
}), !wistiaCaptions || !wistiaCaptions.captionContent)
|
|
3876
|
+
return devLog.debug("Wistia caption field is missing or has no content, returning empty transform", {
|
|
3877
|
+
fieldPath: context.fieldPath,
|
|
3878
|
+
value: wistiaCaptions
|
|
3879
|
+
}), {
|
|
3880
|
+
_type: "translatable.wistia.captions",
|
|
3881
|
+
translationText: "",
|
|
3882
|
+
_originalMetadata: {
|
|
3883
|
+
captionFileName: wistiaCaptions?.captionFileName,
|
|
3884
|
+
isoLanguage: wistiaCaptions?.isoLanguage,
|
|
3885
|
+
captionUploadStatus: wistiaCaptions?.captionUploadStatus,
|
|
3886
|
+
captionUploadMessage: wistiaCaptions?.captionUploadMessage,
|
|
3887
|
+
_originalIndex: 0
|
|
3888
|
+
},
|
|
3889
|
+
_srtCues: []
|
|
3890
|
+
};
|
|
3891
|
+
devLog.info("Applying Wistia caption transform", {
|
|
3892
|
+
fieldPath: context.fieldPath,
|
|
3893
|
+
captionContentPreview: wistiaCaptions.captionContent.substring(0, 200)
|
|
3894
|
+
});
|
|
3895
|
+
const srtCues = parseSRTContent(wistiaCaptions.captionContent);
|
|
3896
|
+
devLog.debug("Parsed SRT cues:", {
|
|
3897
|
+
fieldPath: context.fieldPath,
|
|
3898
|
+
cueCount: srtCues.length,
|
|
3899
|
+
firstCue: srtCues.length > 0 ? srtCues[0] : null
|
|
3900
|
+
});
|
|
3901
|
+
const translationText = serializeWistiaCaptionText(srtCues), originalMetadata = {
|
|
3902
|
+
captionFileName: wistiaCaptions.captionFileName,
|
|
3903
|
+
isoLanguage: wistiaCaptions.isoLanguage,
|
|
3904
|
+
captionUploadStatus: wistiaCaptions.captionUploadStatus,
|
|
3905
|
+
captionUploadMessage: wistiaCaptions.captionUploadMessage,
|
|
3906
|
+
_originalIndex: 0
|
|
3907
|
+
}, srtCuesMetadata = srtCues.map((cue) => ({
|
|
3908
|
+
index: cue.index,
|
|
3909
|
+
startTime: cue.startTime,
|
|
3910
|
+
endTime: cue.endTime
|
|
3911
|
+
}));
|
|
3912
|
+
return devLog.info("Wistia captions serialized:", {
|
|
3913
|
+
fieldPath: context.fieldPath,
|
|
3914
|
+
translationTextLength: translationText.length,
|
|
3915
|
+
translationTextPreview: translationText.substring(0, 200) + (translationText.length > 200 ? "..." : ""),
|
|
3916
|
+
cueCount: srtCues.length
|
|
3917
|
+
}), {
|
|
3918
|
+
_type: "translatable.wistia.captions",
|
|
3919
|
+
translationText,
|
|
3920
|
+
_originalMetadata: originalMetadata,
|
|
3921
|
+
_srtCues: srtCuesMetadata
|
|
3922
|
+
};
|
|
3923
|
+
},
|
|
3924
|
+
fromTranslatable: (translatedValue, originalValue, context) => {
|
|
3925
|
+
if (devLog.info("Wistia captions fromTranslatable - input:", {
|
|
3926
|
+
fieldPath: context.fieldPath,
|
|
3927
|
+
valueType: typeof translatedValue,
|
|
3928
|
+
isCorrectType: translatedValue._type === "translatable.wistia.captions",
|
|
3929
|
+
hasText: !!translatedValue.translationText,
|
|
3930
|
+
hasMetadata: !!translatedValue._originalMetadata,
|
|
3931
|
+
hasCues: Array.isArray(translatedValue._srtCues)
|
|
3932
|
+
}), !translatedValue || translatedValue._type !== "translatable.wistia.captions" || !translatedValue._originalMetadata || !Array.isArray(translatedValue._srtCues))
|
|
3933
|
+
return devLog.debug("Value is not a translatable Wistia caption object, returning original", {
|
|
3934
|
+
fieldPath: context.fieldPath,
|
|
3935
|
+
valueType: typeof translatedValue,
|
|
3936
|
+
translatedValue
|
|
3937
|
+
}), originalValue || {};
|
|
3938
|
+
devLog.info("Processing translated Wistia captions", {
|
|
3939
|
+
fieldPath: context.fieldPath,
|
|
3940
|
+
cueCount: translatedValue._srtCues.length
|
|
3941
|
+
});
|
|
3942
|
+
const { translationText, _originalMetadata, _srtCues } = translatedValue;
|
|
3943
|
+
devLog.info("Wistia captions translationText:", {
|
|
3944
|
+
fieldPath: context.fieldPath,
|
|
3945
|
+
translationTextLength: translationText.length,
|
|
3946
|
+
translationTextPreview: translationText.substring(0, 200) + (translationText.length > 200 ? "..." : "")
|
|
3947
|
+
});
|
|
3948
|
+
const reconstructedCaptionContent = deserializeWistiaCaptionText(translationText, _srtCues), documentLanguage = getDocumentLanguage(context.document);
|
|
3949
|
+
let updatedIsoLanguage = _originalMetadata.isoLanguage;
|
|
3950
|
+
if (documentLanguage) {
|
|
3951
|
+
const isoCode = normalizeToIsoLanguageCode(documentLanguage);
|
|
3952
|
+
isoCode ? (updatedIsoLanguage = isoCode, devLog.debug("Updated isoLanguage based on document language", {
|
|
3953
|
+
fieldPath: context.fieldPath,
|
|
3954
|
+
documentLanguage,
|
|
3955
|
+
isoCode
|
|
3956
|
+
})) : devLog.debug("Could not convert document language to ISO code, preserving original", {
|
|
3957
|
+
fieldPath: context.fieldPath,
|
|
3958
|
+
documentLanguage,
|
|
3959
|
+
originalIsoLanguage: _originalMetadata.isoLanguage
|
|
3960
|
+
});
|
|
3961
|
+
} else
|
|
3962
|
+
devLog.debug("Document language field not found, preserving original isoLanguage", {
|
|
3963
|
+
fieldPath: context.fieldPath,
|
|
3964
|
+
originalIsoLanguage: _originalMetadata.isoLanguage
|
|
3965
|
+
});
|
|
3966
|
+
const result = {
|
|
3967
|
+
captionContent: reconstructedCaptionContent,
|
|
3968
|
+
captionFileName: _originalMetadata.captionFileName,
|
|
3969
|
+
isoLanguage: updatedIsoLanguage,
|
|
3970
|
+
captionUploadStatus: _originalMetadata.captionUploadStatus,
|
|
3971
|
+
captionUploadMessage: _originalMetadata.captionUploadMessage
|
|
3972
|
+
};
|
|
3973
|
+
return devLog.info("Wistia captions final result:", {
|
|
3974
|
+
fieldPath: context.fieldPath,
|
|
3975
|
+
hasCaptionContent: !!result.captionContent,
|
|
3976
|
+
captionContentLength: result.captionContent?.length ?? 0,
|
|
3977
|
+
isoLanguage: result.isoLanguage
|
|
3978
|
+
}), result;
|
|
3979
|
+
}
|
|
3980
|
+
};
|
|
3981
|
+
function parseSRTContent(content) {
|
|
3982
|
+
if (devLog.debug("parseSRTContent - input:", {
|
|
3983
|
+
contentLength: content.length,
|
|
3984
|
+
contentPreview: content.substring(0, 100) + (content.length > 100 ? "..." : "")
|
|
3985
|
+
}), !content || content.trim() === "")
|
|
3986
|
+
return [];
|
|
3987
|
+
const cues = [], cueBlocks = content.split(/\n\s*\n/);
|
|
3988
|
+
for (const block of cueBlocks) {
|
|
3989
|
+
const lines = block.trim().split(`
|
|
3990
|
+
`);
|
|
3991
|
+
if (lines.length < 3)
|
|
3992
|
+
continue;
|
|
3993
|
+
const cueNumber = Number.parseInt(lines[0].trim(), 10);
|
|
3994
|
+
if (Number.isNaN(cueNumber)) {
|
|
3995
|
+
devLog.warn("Invalid SRT cue number", { line: lines[0] });
|
|
3996
|
+
continue;
|
|
3997
|
+
}
|
|
3998
|
+
const timestampLine = lines[1].trim(), timestampMatch = timestampLine.match(/^(\d{2}:\d{2}:\d{2}[,.]\d{3})\s*-->\s*(\d{2}:\d{2}:\d{2}[,.]\d{3})$/);
|
|
3999
|
+
if (!timestampMatch) {
|
|
4000
|
+
devLog.warn("Invalid SRT timestamp format", { line: timestampLine });
|
|
4001
|
+
continue;
|
|
4002
|
+
}
|
|
4003
|
+
const startTime = timestampMatch[1].replace(".", ","), endTime = timestampMatch[2].replace(".", ","), text = lines.slice(2).join(`
|
|
4004
|
+
`).trim();
|
|
4005
|
+
text && cues.push({
|
|
4006
|
+
index: cueNumber,
|
|
4007
|
+
startTime,
|
|
4008
|
+
endTime,
|
|
4009
|
+
text
|
|
4010
|
+
});
|
|
4011
|
+
}
|
|
4012
|
+
return devLog.debug("parseSRTContent - output:", {
|
|
4013
|
+
cueCount: cues.length,
|
|
4014
|
+
firstCue: cues.length > 0 ? cues[0] : null
|
|
4015
|
+
}), cues;
|
|
4016
|
+
}
|
|
4017
|
+
function serializeWistiaCaptionText(cues) {
|
|
4018
|
+
if (devLog.debug("serializeWistiaCaptionText - input:", {
|
|
4019
|
+
cuesCount: cues.length,
|
|
4020
|
+
firstCue: cues.length > 0 ? cues[0] : null
|
|
4021
|
+
}), !Array.isArray(cues) || cues.length === 0)
|
|
4022
|
+
return "";
|
|
4023
|
+
const result = cues.map((cue) => (devLog.debug(`Processing cue ${cue.index}:`, {
|
|
4024
|
+
text: cue.text,
|
|
4025
|
+
hasText: typeof cue.text == "string"
|
|
4026
|
+
}), `[${cue.index}] ${cue.text}`)).join(`
|
|
4027
|
+
`);
|
|
4028
|
+
return devLog.debug("serializeWistiaCaptionText - output:", {
|
|
4029
|
+
resultLength: result.length,
|
|
4030
|
+
resultPreview: result.substring(0, 100) + (result.length > 100 ? "..." : "")
|
|
4031
|
+
}), result;
|
|
4032
|
+
}
|
|
4033
|
+
function deserializeWistiaCaptionText(translationText, srtCuesMetadata) {
|
|
4034
|
+
if (devLog.debug("deserializeWistiaCaptionText - input:", {
|
|
4035
|
+
translationTextLength: translationText.length,
|
|
4036
|
+
translationTextPreview: translationText.substring(0, 100) + (translationText.length > 100 ? "..." : ""),
|
|
4037
|
+
metadataCount: srtCuesMetadata.length,
|
|
4038
|
+
metadataExample: srtCuesMetadata.length > 0 ? srtCuesMetadata[0] : null
|
|
4039
|
+
}), !translationText || translationText.trim() === "")
|
|
4040
|
+
return "";
|
|
4041
|
+
const result = [], lines = translationText.split(`
|
|
4042
|
+
`), lineRegex = /^\[(\d+)\]\s*(.*)$/, metadataByIndex = /* @__PURE__ */ new Map();
|
|
4043
|
+
for (const metadata of srtCuesMetadata)
|
|
4044
|
+
metadataByIndex.set(metadata.index, { startTime: metadata.startTime, endTime: metadata.endTime });
|
|
4045
|
+
for (const line of lines) {
|
|
4046
|
+
const match = line.match(lineRegex);
|
|
4047
|
+
if (!match) {
|
|
4048
|
+
devLog.warn("Failed to parse caption line", { line });
|
|
4049
|
+
continue;
|
|
4050
|
+
}
|
|
4051
|
+
const index = Number.parseInt(match[1], 10), text = match[2];
|
|
4052
|
+
devLog.debug("Found caption:", {
|
|
4053
|
+
index,
|
|
4054
|
+
text
|
|
4055
|
+
});
|
|
4056
|
+
const metadata = metadataByIndex.get(index);
|
|
4057
|
+
metadata ? (devLog.debug(`Found matching metadata for index ${index}`), result.push(`${index}`), result.push(`${metadata.startTime} --> ${metadata.endTime}`), result.push(text), result.push("")) : devLog.warn("Missing metadata for caption", { index });
|
|
4058
|
+
}
|
|
4059
|
+
result.length > 0 && result[result.length - 1] === "" && result.pop();
|
|
4060
|
+
const reconstructed = result.join(`
|
|
4061
|
+
`);
|
|
4062
|
+
return devLog.debug("deserializeWistiaCaptionText - output:", {
|
|
4063
|
+
resultLength: reconstructed.length,
|
|
4064
|
+
resultPreview: reconstructed.substring(0, 200) + (reconstructed.length > 200 ? "..." : "")
|
|
4065
|
+
}), reconstructed;
|
|
4066
|
+
}
|
|
4067
|
+
function getDocumentLanguage(document) {
|
|
4068
|
+
if (!document || typeof document != "object")
|
|
4069
|
+
return;
|
|
4070
|
+
const languageFieldNames = ["language", "lang", "locale"];
|
|
4071
|
+
for (const fieldName of languageFieldNames)
|
|
4072
|
+
if (fieldName in document && typeof document[fieldName] == "string")
|
|
4073
|
+
return document[fieldName];
|
|
4074
|
+
}
|
|
3802
4075
|
const aiTranslationPlugin = definePlugin((config) => (config.fieldTransforms?.length && (devLog.info(`Registering ${config.fieldTransforms.length} custom field transforms`), config.fieldTransforms.forEach((transform) => {
|
|
3803
4076
|
globalTransformRegistry.register(transform);
|
|
3804
4077
|
})), {
|
|
@@ -3818,6 +4091,7 @@ export {
|
|
|
3818
4091
|
restoreExcludedFields,
|
|
3819
4092
|
richTextTransform,
|
|
3820
4093
|
translationHistorySchema,
|
|
3821
|
-
vttCaptionTransform
|
|
4094
|
+
vttCaptionTransform,
|
|
4095
|
+
wistiaCaptionTransform
|
|
3822
4096
|
};
|
|
3823
4097
|
//# sourceMappingURL=index.js.map
|