@braze/sanity-plugin-ai-translation 0.2.32 → 0.2.36

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.js CHANGED
@@ -1799,6 +1799,146 @@ const getExistingTranslationId = (translationMetadata, targetLanguage) => transl
1799
1799
  })
1800
1800
  });
1801
1801
  };
1802
+ function shouldExcludeField(fieldPath, excludeFields) {
1803
+ for (const pattern of excludeFields) {
1804
+ if (fieldPath === pattern)
1805
+ return !0;
1806
+ if (pattern.startsWith("**.")) {
1807
+ const fieldName = pattern.substring(3), segments = fieldPath.split(".");
1808
+ if (segments[segments.length - 1]?.split("[")[0] === fieldName)
1809
+ return !0;
1810
+ } else if (!pattern.includes(".") && !pattern.includes("[")) {
1811
+ const segments = fieldPath.split(".");
1812
+ if (segments[segments.length - 1]?.split("[")[0] === pattern)
1813
+ return !0;
1814
+ } else if (pattern.includes("[*]")) {
1815
+ const regexPattern = pattern.replace(/\[/g, "\\[").replace(/\]/g, "\\]").replace(/\\\[\*\\\]/g, "\\[\\d+\\]");
1816
+ if (new RegExp(`^${regexPattern}$`).test(fieldPath))
1817
+ return !0;
1818
+ }
1819
+ }
1820
+ return !1;
1821
+ }
1822
+ function filterExcludedFields(document, excludeFields = [], currentPath = "") {
1823
+ if (!excludeFields.length)
1824
+ return {
1825
+ filteredDocument: document,
1826
+ excludedValues: {}
1827
+ };
1828
+ const filteredDocument = {}, excludedValues = {};
1829
+ let hasExclusions = !1;
1830
+ for (const [key, value] of Object.entries(document)) {
1831
+ const fieldPath = currentPath ? `${currentPath}.${key}` : key;
1832
+ if (shouldExcludeField(fieldPath, excludeFields)) {
1833
+ excludedValues[fieldPath] = value, hasExclusions = !0, devLog.debug("Excluding field from translation", {
1834
+ fieldPath,
1835
+ fieldType: typeof value,
1836
+ value: typeof value == "string" ? value : "[object]"
1837
+ });
1838
+ continue;
1839
+ }
1840
+ if (value && typeof value == "object" && !Array.isArray(value) && value._type !== "reference") {
1841
+ const nested = filterExcludedFields(value, excludeFields, fieldPath);
1842
+ filteredDocument[key] = nested.filteredDocument, Object.assign(excludedValues, nested.excludedValues), Object.keys(nested.excludedValues).length > 0 && (hasExclusions = !0);
1843
+ } else if (Array.isArray(value)) {
1844
+ const filteredArray = [];
1845
+ let arrayHasExclusions = !1;
1846
+ for (let i = 0; i < value.length; i++) {
1847
+ const arrayItem = value[i], itemPath = `${fieldPath}[${i}]`;
1848
+ if (arrayItem && typeof arrayItem == "object" && !Array.isArray(arrayItem) && arrayItem._type !== "reference") {
1849
+ const nested = filterExcludedFields(arrayItem, excludeFields, itemPath);
1850
+ filteredArray[i] = nested.filteredDocument, Object.assign(excludedValues, nested.excludedValues), Object.keys(nested.excludedValues).length > 0 && (arrayHasExclusions = !0);
1851
+ } else
1852
+ filteredArray[i] = arrayItem;
1853
+ }
1854
+ filteredDocument[key] = filteredArray, arrayHasExclusions && (hasExclusions = !0);
1855
+ } else
1856
+ filteredDocument[key] = value;
1857
+ }
1858
+ return hasExclusions && devLog.info("Filtered excluded fields from document", {
1859
+ documentId: document._id || "unknown",
1860
+ excludedFieldCount: Object.keys(excludedValues).length,
1861
+ excludedFields: Object.keys(excludedValues)
1862
+ }), {
1863
+ filteredDocument,
1864
+ excludedValues
1865
+ };
1866
+ }
1867
+ function restoreExcludedFields(translatedDocument, excludedValues) {
1868
+ if (!Object.keys(excludedValues).length)
1869
+ return translatedDocument;
1870
+ const result = { ...translatedDocument };
1871
+ for (const [fieldPath, value] of Object.entries(excludedValues))
1872
+ setNestedValue(result, fieldPath, value);
1873
+ return devLog.debug("Restored excluded fields to translated document", {
1874
+ documentId: result._id || "unknown",
1875
+ restoredFieldCount: Object.keys(excludedValues).length,
1876
+ restoredFields: Object.keys(excludedValues)
1877
+ }), result;
1878
+ }
1879
+ function setNestedValue(obj, path, value) {
1880
+ const segments = path.split(".");
1881
+ let current = obj;
1882
+ for (let i = 0; i < segments.length - 1; i++) {
1883
+ const segment = segments[i], arrayMatch2 = segment.match(/^(.+)\[(\d+)\]$/);
1884
+ if (arrayMatch2) {
1885
+ const [, fieldName, indexStr] = arrayMatch2, index = Number.parseInt(indexStr, 10);
1886
+ for ((!(fieldName in current) || !Array.isArray(current[fieldName])) && (current[fieldName] = []); current[fieldName].length <= index; )
1887
+ current[fieldName].push({});
1888
+ current = current[fieldName][index];
1889
+ } else
1890
+ (!(segment in current) || typeof current[segment] != "object") && (current[segment] = {}), current = current[segment];
1891
+ }
1892
+ const finalSegment = segments[segments.length - 1], arrayMatch = finalSegment.match(/^(.+)\[(\d+)\]$/);
1893
+ if (arrayMatch) {
1894
+ const [, fieldName, indexStr] = arrayMatch, index = Number.parseInt(indexStr, 10);
1895
+ for ((!(fieldName in current) || !Array.isArray(current[fieldName])) && (current[fieldName] = []); current[fieldName].length <= index; )
1896
+ current[fieldName].push(null);
1897
+ current[fieldName][index] = value;
1898
+ } else
1899
+ current[finalSegment] = value;
1900
+ }
1901
+ const COMMON_ENUM_FIELDS = [
1902
+ "status",
1903
+ "state",
1904
+ "type",
1905
+ "category",
1906
+ "priority",
1907
+ "visibility",
1908
+ "role",
1909
+ "permission",
1910
+ "level",
1911
+ "mode",
1912
+ "format",
1913
+ "variant",
1914
+ "size",
1915
+ "theme",
1916
+ "kind",
1917
+ "phase",
1918
+ "stage"
1919
+ ], PREDEFINED_EXCLUSIONS = {
1920
+ /** Common enum and status fields */
1921
+ enums: [...COMMON_ENUM_FIELDS],
1922
+ /** All metadata and system fields (starting with _) */
1923
+ metadata: ["_id", "_type", "_rev", "_createdAt", "_updatedAt"],
1924
+ /** Common technical and configuration fields */
1925
+ technical: [
1926
+ "slug",
1927
+ "handle",
1928
+ "key",
1929
+ "code",
1930
+ "id",
1931
+ "uuid",
1932
+ "token",
1933
+ "hash",
1934
+ "checksum",
1935
+ "version",
1936
+ "config",
1937
+ "settings"
1938
+ ],
1939
+ /** SEO and URL-related fields that often shouldn't be translated */
1940
+ seo: ["slug", "handle", "permalink", "canonical", "redirect", "robots", "sitemap"]
1941
+ };
1802
1942
  function applyTransforms({
1803
1943
  document,
1804
1944
  registry,
@@ -1961,9 +2101,10 @@ class TransformRegistry {
1961
2101
  }), matches[0];
1962
2102
  }
1963
2103
  }
1964
- const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKey, request) => (request.model || "gpt-5") === "gpt-5" ? await callGPT5ResponsesAPI(apiKey, request) : await callChatCompletionsAPI(apiKey, request), callGPT5ResponsesAPI = async (apiKey, request) => {
2104
+ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKey, request) => (request.model || "gpt-5").startsWith("gpt-5") ? await callGPT5ResponsesAPI(apiKey, request) : await callChatCompletionsAPI(apiKey, request), callGPT5ResponsesAPI = async (apiKey, request) => {
1965
2105
  const model = request.model || "gpt-5", requestBody = {
1966
- model: "gpt-5.1",
2106
+ model: model === "gpt-5" ? "gpt-5.1" : model,
2107
+ // Map gpt-5 to gpt-5.1, use others as-is
1967
2108
  input: request.messages
1968
2109
  // Uses 'input' instead of 'messages'
1969
2110
  };
@@ -1998,7 +2139,8 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
1998
2139
  prompt: result.usage?.input_tokens || 0,
1999
2140
  completion: result.usage?.output_tokens || 0,
2000
2141
  reasoning: result.usage?.output_tokens_details?.reasoning_tokens || 0,
2001
- total: result.usage?.total_tokens || 0
2142
+ total: result.usage?.total_tokens || 0,
2143
+ cached: result.usage?.input_tokens_details?.cached_tokens || 0
2002
2144
  }, cost = calculateCost(tokensUsed, model);
2003
2145
  let content = "";
2004
2146
  if (result.output && Array.isArray(result.output)) {
@@ -2043,7 +2185,8 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
2043
2185
  prompt: usage.prompt_tokens || 0,
2044
2186
  completion: usage.completion_tokens || 0,
2045
2187
  reasoning: usage.reasoning_tokens || 0,
2046
- total: usage.total_tokens || 0
2188
+ total: usage.total_tokens || 0,
2189
+ cached: usage.prompt_tokens_details?.cached_tokens || 0
2047
2190
  }, cost = calculateCost(tokensUsed, model);
2048
2191
  return {
2049
2192
  content: result.choices[0].message.content || "",
@@ -2053,27 +2196,47 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
2053
2196
  }, calculateCost = (tokens, model) => {
2054
2197
  const pricing = {
2055
2198
  "gpt-5": {
2056
- input: 2e-6,
2199
+ input: 125e-8,
2200
+ output: 1e-5,
2201
+ reasoning: 0
2202
+ },
2203
+ "gpt-5.1": {
2204
+ input: 125e-8,
2057
2205
  output: 1e-5,
2058
- reasoning: 5e-6
2206
+ reasoning: 0
2207
+ },
2208
+ "gpt-5-mini": {
2209
+ input: 25e-8,
2210
+ output: 2e-6,
2211
+ reasoning: 0
2212
+ },
2213
+ "gpt-5-nano": {
2214
+ input: 50000000000000004e-24,
2215
+ output: 40000000000000003e-23,
2216
+ reasoning: 0
2217
+ },
2218
+ "gpt-5-pro": {
2219
+ input: 15e-6,
2220
+ output: 12e-5,
2221
+ reasoning: 0
2059
2222
  },
2060
2223
  "gpt-4o": {
2061
- input: 5e-6,
2062
- output: 14999999999999999e-21,
2224
+ input: 25e-7,
2225
+ output: 1e-5,
2063
2226
  reasoning: 0
2064
2227
  },
2065
2228
  "gpt-4-turbo": {
2066
2229
  input: 1e-5,
2067
- output: 29999999999999997e-21,
2230
+ output: 3e-5,
2068
2231
  reasoning: 0
2069
2232
  },
2070
2233
  "gpt-4": {
2071
- input: 29999999999999997e-21,
2072
- output: 59999999999999995e-21,
2234
+ input: 3e-5,
2235
+ output: 6e-5,
2073
2236
  reasoning: 0
2074
2237
  }
2075
- }, modelPricing = pricing[model] || pricing["gpt-5"];
2076
- return tokens.prompt * modelPricing.input + tokens.completion * modelPricing.output + tokens.reasoning * modelPricing.reasoning;
2238
+ }, modelPricing = pricing[model] || pricing["gpt-5"], cachedInputRate = model === "gpt-5.1" ? 0.125 / 1e6 : modelPricing.input;
2239
+ return Math.max(0, tokens.prompt - tokens.cached) * modelPricing.input + tokens.cached * cachedInputRate + tokens.completion * modelPricing.output + tokens.reasoning * modelPricing.reasoning;
2077
2240
  }, deserializeDocument = (originalDoc, reviewedText) => {
2078
2241
  const parsed = parseReviewFormat(reviewedText), result = cloneDeep(originalDoc);
2079
2242
  return applyTextChanges(result, parsed), result;
@@ -2089,7 +2252,7 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
2089
2252
  type: "field",
2090
2253
  name: fieldName,
2091
2254
  text: unescapeXml(text2),
2092
- attributes: {},
2255
+ attributes: { type: "string" },
2093
2256
  children: []
2094
2257
  };
2095
2258
  if (stack.length > 0) {
@@ -2162,7 +2325,13 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
2162
2325
  parent && parent.children.push(node);
2163
2326
  } else
2164
2327
  root.push(node);
2165
- } else
2328
+ } else if (attributes.type === "string" && content && content.includes(closingTag))
2329
+ if (node.text = unescapeXml(content.replace(closingTag, "")), stack.length > 0) {
2330
+ const parent = stack[stack.length - 1];
2331
+ parent && parent.children.push(node);
2332
+ } else
2333
+ root.push(node);
2334
+ else
2166
2335
  stack.push(node);
2167
2336
  continue;
2168
2337
  }
@@ -2218,7 +2387,10 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
2218
2387
  });
2219
2388
  return;
2220
2389
  }
2221
- node.text !== void 0 && !node.attributes.readonly && set(doc, path, node.text);
2390
+ node.text !== void 0 && !node.attributes.readonly && (get(doc, path) !== void 0 ? set(doc, path, node.text) : devLog.debug("Skipping field not present in original document", {
2391
+ path: path.join("."),
2392
+ attemptedValue: node.text
2393
+ }));
2222
2394
  }, applyBlockChanges = (block, blockNode) => {
2223
2395
  if (blockNode.attributes._key && block._key !== blockNode.attributes._key) {
2224
2396
  devLog.warn("Block key mismatch, skipping", {
@@ -2249,7 +2421,10 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
2249
2421
  });
2250
2422
  return;
2251
2423
  }
2252
- fieldNode.text !== void 0 && !fieldNode.attributes.readonly && (block[fieldName] = fieldNode.text);
2424
+ fieldNode.text !== void 0 && !fieldNode.attributes.readonly && (fieldName in block ? block[fieldName] = fieldNode.text : devLog.debug("Skipping block field not present in original", {
2425
+ fieldName,
2426
+ attemptedValue: fieldNode.text
2427
+ }));
2253
2428
  }, applySpanChanges = (span, spanNode) => {
2254
2429
  if (spanNode.attributes._key && span._key !== spanNode.attributes._key) {
2255
2430
  devLog.warn("Span key mismatch, skipping", {
@@ -2294,7 +2469,7 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
2294
2469
  return;
2295
2470
  }
2296
2471
  if (typeof value == "string") {
2297
- value.length > 0 && lines.push(`${fieldName}: ${escapeXml(value)}`);
2472
+ value.length > 0 && lines.push(`${indentStr}<field name="${fieldName}" type="string">${escapeXml(value)}</field>`);
2298
2473
  return;
2299
2474
  }
2300
2475
  (typeof value == "number" || typeof value == "boolean") && lines.push(`${indentStr}<field name="${fieldName}" type="${typeof value}" readonly="true">${value}</field>`);
@@ -2331,7 +2506,7 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
2331
2506
  return;
2332
2507
  }
2333
2508
  if (typeof value == "string" && value.length > 0) {
2334
- lines.push(`${indentStr}<field name="${fieldName}">${escapeXml(value)}</field>`);
2509
+ lines.push(`${indentStr}<field name="${fieldName}" type="string">${escapeXml(value)}</field>`);
2335
2510
  return;
2336
2511
  }
2337
2512
  (typeof value == "number" || typeof value == "boolean") && lines.push(`${indentStr}<field name="${fieldName}" type="${typeof value}" readonly="true">${value}</field>`);
@@ -2473,7 +2648,19 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
2473
2648
  `Structure validation failed: ${unexpectedErrors.length} errors. First: ${unexpectedErrors[0].type} at ${unexpectedErrors[0].path.join(".")}`
2474
2649
  );
2475
2650
  }
2476
- return setJobStatus(dispatch, sourceDocument._id, targetLanguage, "translating_with_openai", {
2651
+ const translationValidation = validateTranslationCompleteness(sourceDocument, result, languageFieldName);
2652
+ return translationValidation.valid || (devLog.warn("Translation completeness warning", {
2653
+ ...logContext,
2654
+ skippedFields: translationValidation.skippedFields,
2655
+ warning: "GPT-5 may have skipped some important fields"
2656
+ }), devLog.info("Skipped field details", {
2657
+ ...logContext,
2658
+ skippedFields: translationValidation.skippedFields.map((field) => ({
2659
+ path: field.path,
2660
+ originalValue: field.originalValue,
2661
+ translatedValue: field.translatedValue
2662
+ }))
2663
+ })), setJobStatus(dispatch, sourceDocument._id, targetLanguage, "translating_with_openai", {
2477
2664
  tokensUsed: response.tokensUsed.total,
2478
2665
  estimatedCost: response.cost
2479
2666
  }), result;
@@ -2514,12 +2701,19 @@ ${sourceText}
2514
2701
 
2515
2702
  ## Instructions
2516
2703
 
2517
- 1. Translate all text content into ${targetLanguage}
2518
- 2. Follow the guidelines and terminology requirements above
2519
- 3. Preserve protected phrases exactly as they appear
2520
- 4. Return the document in THE EXACT SAME FORMAT
2521
- 5. **CRITICAL**: Only modify text content between tags
2522
- 6. Never add, remove, or change any tags, attributes, or structure
2704
+ 1. **TRANSLATE ALL TEXT FIELDS**: Every field marked with type="string" or containing text must be translated
2705
+ 2. **DO NOT SKIP SIMPLE FIELDS**: Pay special attention to simple fields like "title", "name", "description" - these are often the most important
2706
+ 3. Follow the guidelines and terminology requirements above
2707
+ 4. Preserve protected phrases exactly as they appear
2708
+ 5. Return the document in THE EXACT SAME FORMAT
2709
+ 6. **CRITICAL**: Only modify text content between tags
2710
+ 7. Never add, remove, or change any tags, attributes, or structure
2711
+
2712
+ **Common fields that must always be translated:**
2713
+ - title, name, heading, label
2714
+ - description, caption, alt text
2715
+ - All text within <field> tags
2716
+ - All text within <span> tags
2523
2717
 
2524
2718
  If you used significant reasoning to make translation decisions, you may include a brief summary:
2525
2719
  <reasoning>
@@ -2527,6 +2721,43 @@ Brief summary of key translation decisions and reasoning
2527
2721
  </reasoning>
2528
2722
 
2529
2723
  Then return the translated document:`;
2724
+ }, validateTranslationCompleteness = (sourceDoc, translatedDoc, languageFieldName) => {
2725
+ const skippedFields = [], checkField = (source, translated, path = []) => {
2726
+ if (path.length > 0) {
2727
+ const fieldName = path[path.length - 1];
2728
+ if (fieldName.startsWith("_") || fieldName === languageFieldName)
2729
+ return;
2730
+ }
2731
+ if (typeof source == "string" && typeof translated == "string") {
2732
+ if (source.length > 0 && source === translated) {
2733
+ const fieldName = path[path.length - 1] || "unknown";
2734
+ (["title", "name", "heading", "label", "description", "caption", "alt"].includes(
2735
+ fieldName
2736
+ ) || source.length > 10) && skippedFields.push({
2737
+ path: path.join("."),
2738
+ originalValue: source,
2739
+ translatedValue: translated
2740
+ });
2741
+ }
2742
+ return;
2743
+ }
2744
+ if (source && translated && typeof source == "object" && typeof translated == "object") {
2745
+ if (Array.isArray(source) && Array.isArray(translated)) {
2746
+ const maxLength = Math.min(source.length, translated.length);
2747
+ for (let i = 0; i < maxLength; i++)
2748
+ checkField(source[i], translated[i], [...path, String(i)]);
2749
+ } else if (!Array.isArray(source) && !Array.isArray(translated))
2750
+ for (const key of Object.keys(source))
2751
+ !key.startsWith("_") && key !== languageFieldName && checkField(source[key], translated[key], [
2752
+ ...path,
2753
+ key
2754
+ ]);
2755
+ }
2756
+ };
2757
+ return checkField(sourceDoc, translatedDoc), {
2758
+ valid: skippedFields.length === 0,
2759
+ skippedFields
2760
+ };
2530
2761
  }, assembleLanguageSpecs = ({
2531
2762
  sourceDocument,
2532
2763
  languageFieldName,
@@ -2647,14 +2878,30 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2647
2878
  translationPrompts,
2648
2879
  translationMode,
2649
2880
  openaiConfig,
2650
- releaseId
2881
+ releaseId,
2882
+ excludeFields = []
2651
2883
  }) => {
2652
- devLog.debug("Applying field transforms before translation", {
2884
+ devLog.info("Filtering excluded fields before translation", {
2885
+ documentId: sourceDocument._id,
2886
+ documentType: sourceDocument._type,
2887
+ excludeFields,
2888
+ excludeFieldsLength: excludeFields?.length ?? 0,
2889
+ sourceHasTitle: "title" in sourceDocument,
2890
+ sourceTitleValue: sourceDocument.title
2891
+ });
2892
+ const { filteredDocument, excludedValues } = filterExcludedFields(sourceDocument, excludeFields);
2893
+ devLog.info("After filtering excluded fields", {
2894
+ documentId: sourceDocument._id,
2895
+ filteredHasTitle: "title" in filteredDocument,
2896
+ filteredTitleValue: filteredDocument.title,
2897
+ excludedValuesKeys: Object.keys(excludedValues),
2898
+ excludedValues
2899
+ }), devLog.debug("Applying field transforms before translation", {
2653
2900
  documentId: sourceDocument._id,
2654
2901
  documentType: sourceDocument._type
2655
2902
  });
2656
2903
  const { transformedDocument, originalStructure } = applyTransforms({
2657
- document: sourceDocument,
2904
+ document: filteredDocument,
2658
2905
  registry: globalTransformRegistry,
2659
2906
  schemaType: sourceDocument._type
2660
2907
  });
@@ -2695,12 +2942,26 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2695
2942
  documentId: sourceDocument._id,
2696
2943
  documentType: sourceDocument._type
2697
2944
  });
2698
- const finalDoc = restoreTransforms({
2945
+ const transformedResult = restoreTransforms({
2699
2946
  translatedDocument: translatedDoc,
2700
2947
  originalStructure,
2701
2948
  registry: globalTransformRegistry,
2702
2949
  schemaType: sourceDocument._type
2703
- }), targetDocumentPublishedId = existingTranslationId || uuid(), targetDocumentVersionId = `versions.${releaseId}.${targetDocumentPublishedId}`, newDoc = {
2950
+ });
2951
+ devLog.info("Before restoring excluded fields", {
2952
+ documentId: sourceDocument._id,
2953
+ excludedFieldCount: Object.keys(excludedValues).length,
2954
+ excludedValuesKeys: Object.keys(excludedValues),
2955
+ transformedResultHasTitle: "title" in transformedResult,
2956
+ transformedResultTitle: transformedResult.title
2957
+ });
2958
+ const finalDoc = restoreExcludedFields(transformedResult, excludedValues);
2959
+ devLog.info("After restoring excluded fields", {
2960
+ documentId: sourceDocument._id,
2961
+ finalDocHasTitle: "title" in finalDoc,
2962
+ finalDocTitle: finalDoc.title
2963
+ });
2964
+ const targetDocumentPublishedId = existingTranslationId || uuid(), targetDocumentVersionId = `versions.${releaseId}.${targetDocumentPublishedId}`, newDoc = {
2704
2965
  ...finalDoc,
2705
2966
  _id: targetDocumentVersionId
2706
2967
  };
@@ -2721,7 +2982,8 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2721
2982
  translationPrompts,
2722
2983
  translationMode,
2723
2984
  openaiConfig,
2724
- releaseId
2985
+ releaseId,
2986
+ excludeFields
2725
2987
  }) => {
2726
2988
  const logContext = {
2727
2989
  documentId: sourceDocument._id,
@@ -2757,7 +3019,8 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2757
3019
  translationPrompts,
2758
3020
  translationMode,
2759
3021
  openaiConfig,
2760
- releaseId
3022
+ releaseId,
3023
+ excludeFields
2761
3024
  }),
2762
3025
  {
2763
3026
  maxRetries: 3,
@@ -2823,7 +3086,8 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2823
3086
  references = [],
2824
3087
  maxConcurrentTranslationJobs,
2825
3088
  translationMode,
2826
- openaiConfig
3089
+ openaiConfig,
3090
+ excludeFields
2827
3091
  }) => {
2828
3092
  const logContext = {
2829
3093
  documentId: sourceDocument._id,
@@ -2889,7 +3153,8 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2889
3153
  translationPrompts,
2890
3154
  translationMode,
2891
3155
  openaiConfig,
2892
- releaseId: releaseId2
3156
+ releaseId: releaseId2,
3157
+ excludeFields
2893
3158
  });
2894
3159
  versionedTargetDocumentId && versionedTargetDocumentIds.push(versionedTargetDocumentId);
2895
3160
  }
@@ -2962,7 +3227,8 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2962
3227
  maxConcurrentTranslationJobs: config.maxConcurrentTranslationJobs,
2963
3228
  maxReferenceDepth: config.maxReferenceDepth,
2964
3229
  translationMode,
2965
- openaiConfig: config.openai
3230
+ openaiConfig: config.openai,
3231
+ excludeFields: config.excludeFields
2966
3232
  });
2967
3233
  }, [
2968
3234
  document,
@@ -2974,6 +3240,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2974
3240
  config.maxConcurrentTranslationJobs,
2975
3241
  config.maxReferenceDepth,
2976
3242
  config.openai,
3243
+ config.excludeFields,
2977
3244
  references,
2978
3245
  shouldOverwriteFreshTranslations,
2979
3246
  translationMode
@@ -3955,7 +4222,11 @@ const aiTranslationPlugin = definePlugin((config) => (config.fieldTransforms?.le
3955
4222
  }
3956
4223
  })), translationHistorySchema = translationHistory;
3957
4224
  export {
4225
+ COMMON_ENUM_FIELDS,
4226
+ PREDEFINED_EXCLUSIONS,
3958
4227
  aiTranslationPlugin,
4228
+ filterExcludedFields,
4229
+ restoreExcludedFields,
3959
4230
  richTextTransform,
3960
4231
  translationHistorySchema,
3961
4232
  vttCaptionTransform