@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.d.ts +146 -1
- package/dist/index.js +307 -36
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
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")
|
|
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:
|
|
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:
|
|
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:
|
|
2062
|
-
output:
|
|
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:
|
|
2230
|
+
output: 3e-5,
|
|
2068
2231
|
reasoning: 0
|
|
2069
2232
|
},
|
|
2070
2233
|
"gpt-4": {
|
|
2071
|
-
input:
|
|
2072
|
-
output:
|
|
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(`${
|
|
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
|
-
|
|
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.
|
|
2518
|
-
2.
|
|
2519
|
-
3.
|
|
2520
|
-
4.
|
|
2521
|
-
5.
|
|
2522
|
-
6.
|
|
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.
|
|
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:
|
|
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
|
|
2945
|
+
const transformedResult = restoreTransforms({
|
|
2699
2946
|
translatedDocument: translatedDoc,
|
|
2700
2947
|
originalStructure,
|
|
2701
2948
|
registry: globalTransformRegistry,
|
|
2702
2949
|
schemaType: sourceDocument._type
|
|
2703
|
-
})
|
|
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
|