@braze/sanity-plugin-ai-translation 0.2.33 → 0.2.37
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 +137 -0
- package/dist/index.js +224 -34
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.d.ts
CHANGED
|
@@ -38,6 +38,30 @@ export declare const aiTranslationPlugin: Plugin_2<PluginConfig>;
|
|
|
38
38
|
*/
|
|
39
39
|
export declare type AnyFieldTransform = FieldTransform<any, any>;
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Common enum field names that are typically excluded from translation
|
|
43
|
+
* @internal
|
|
44
|
+
*/
|
|
45
|
+
export declare const COMMON_ENUM_FIELDS: readonly [
|
|
46
|
+
"status",
|
|
47
|
+
"state",
|
|
48
|
+
"type",
|
|
49
|
+
"category",
|
|
50
|
+
"priority",
|
|
51
|
+
"visibility",
|
|
52
|
+
"role",
|
|
53
|
+
"permission",
|
|
54
|
+
"level",
|
|
55
|
+
"mode",
|
|
56
|
+
"format",
|
|
57
|
+
"variant",
|
|
58
|
+
"size",
|
|
59
|
+
"theme",
|
|
60
|
+
"kind",
|
|
61
|
+
"phase",
|
|
62
|
+
"stage",
|
|
63
|
+
];
|
|
64
|
+
|
|
41
65
|
/**
|
|
42
66
|
* Criteria for matching fields that should be transformed.
|
|
43
67
|
* @public
|
|
@@ -169,6 +193,36 @@ export declare interface FieldTransform<
|
|
|
169
193
|
) => TOriginal;
|
|
170
194
|
}
|
|
171
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Result of filtering excluded fields from a document
|
|
198
|
+
* @internal
|
|
199
|
+
*/
|
|
200
|
+
export declare interface FilteredDocument<T = any> {
|
|
201
|
+
/** Document with excluded fields removed */
|
|
202
|
+
filteredDocument: T;
|
|
203
|
+
/** Excluded field values that were removed */
|
|
204
|
+
excludedValues: Record<string, any>;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Recursively filters out excluded fields from a document
|
|
209
|
+
* @param document - The document to filter
|
|
210
|
+
* @param excludeFields - Array of field patterns to exclude. Supports:
|
|
211
|
+
* - Field names: 'status' (matches any field named 'status' anywhere)
|
|
212
|
+
* - Exact paths: 'meta.category' (matches only 'category' under 'meta')
|
|
213
|
+
* - Array paths: 'body[0].imageType' (matches 'imageType' in first array item)
|
|
214
|
+
* - Wildcard arrays: 'body[*].imageType' (matches 'imageType' in any array item)
|
|
215
|
+
* - Hierarchical: '**.status' (explicitly matches 'status' anywhere - same as just 'status')
|
|
216
|
+
* @param currentPath - Current path in the document (used internally)
|
|
217
|
+
* @returns Filtered document and excluded values
|
|
218
|
+
* @internal
|
|
219
|
+
*/
|
|
220
|
+
export declare function filterExcludedFields<T extends Record<string, any>>(
|
|
221
|
+
document: T,
|
|
222
|
+
excludeFields?: string[],
|
|
223
|
+
currentPath?: string,
|
|
224
|
+
): FilteredDocument<T>;
|
|
225
|
+
|
|
172
226
|
/**
|
|
173
227
|
* Helper type to infer the original type from a FieldTransform.
|
|
174
228
|
* Useful for type checking and creating wrapper functions.
|
|
@@ -247,8 +301,91 @@ export declare interface PluginConfig {
|
|
|
247
301
|
defaultMode?: TranslationMode;
|
|
248
302
|
};
|
|
249
303
|
fieldTransforms?: AnyFieldTransform[];
|
|
304
|
+
/**
|
|
305
|
+
* Fields to exclude from translation. These fields will be preserved as-is in the translated document.
|
|
306
|
+
* Supports field names (e.g., 'status') and nested field paths (e.g., 'meta.category').
|
|
307
|
+
* Useful for enum fields, IDs, technical values, and other non-translatable content.
|
|
308
|
+
*
|
|
309
|
+
* @example
|
|
310
|
+
* ```typescript
|
|
311
|
+
* excludeFields: ['status', 'category', 'type', 'meta.priority', 'config.mode']
|
|
312
|
+
* ```
|
|
313
|
+
*/
|
|
314
|
+
excludeFields?: string[];
|
|
250
315
|
}
|
|
251
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Predefined exclusion lists for common use cases
|
|
319
|
+
* @internal
|
|
320
|
+
*/
|
|
321
|
+
export declare const PREDEFINED_EXCLUSIONS: {
|
|
322
|
+
/** Common enum and status fields */
|
|
323
|
+
readonly enums: readonly [
|
|
324
|
+
"status",
|
|
325
|
+
"state",
|
|
326
|
+
"type",
|
|
327
|
+
"category",
|
|
328
|
+
"priority",
|
|
329
|
+
"visibility",
|
|
330
|
+
"role",
|
|
331
|
+
"permission",
|
|
332
|
+
"level",
|
|
333
|
+
"mode",
|
|
334
|
+
"format",
|
|
335
|
+
"variant",
|
|
336
|
+
"size",
|
|
337
|
+
"theme",
|
|
338
|
+
"kind",
|
|
339
|
+
"phase",
|
|
340
|
+
"stage",
|
|
341
|
+
];
|
|
342
|
+
/** All metadata and system fields (starting with _) */
|
|
343
|
+
readonly metadata: readonly [
|
|
344
|
+
"_id",
|
|
345
|
+
"_type",
|
|
346
|
+
"_rev",
|
|
347
|
+
"_createdAt",
|
|
348
|
+
"_updatedAt",
|
|
349
|
+
];
|
|
350
|
+
/** Common technical and configuration fields */
|
|
351
|
+
readonly technical: readonly [
|
|
352
|
+
"slug",
|
|
353
|
+
"handle",
|
|
354
|
+
"key",
|
|
355
|
+
"code",
|
|
356
|
+
"id",
|
|
357
|
+
"uuid",
|
|
358
|
+
"token",
|
|
359
|
+
"hash",
|
|
360
|
+
"checksum",
|
|
361
|
+
"version",
|
|
362
|
+
"config",
|
|
363
|
+
"settings",
|
|
364
|
+
];
|
|
365
|
+
/** SEO and URL-related fields that often shouldn't be translated */
|
|
366
|
+
readonly seo: readonly [
|
|
367
|
+
"slug",
|
|
368
|
+
"handle",
|
|
369
|
+
"permalink",
|
|
370
|
+
"canonical",
|
|
371
|
+
"redirect",
|
|
372
|
+
"robots",
|
|
373
|
+
"sitemap",
|
|
374
|
+
];
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Restores previously excluded fields to a translated document
|
|
379
|
+
* @param translatedDocument - The translated document
|
|
380
|
+
* @param excludedValues - The excluded field values to restore
|
|
381
|
+
* @returns Document with excluded fields restored
|
|
382
|
+
* @internal
|
|
383
|
+
*/
|
|
384
|
+
export declare function restoreExcludedFields<T extends Record<string, any>>(
|
|
385
|
+
translatedDocument: T,
|
|
386
|
+
excludedValues: Record<string, any>,
|
|
387
|
+
): T;
|
|
388
|
+
|
|
252
389
|
/**
|
|
253
390
|
* Transform for handling Portable Text (rich text) content using XML-like serialization.
|
|
254
391
|
* This leverages the existing serialization utilities to convert complex rich text
|
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,
|
|
@@ -1999,7 +2139,8 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
|
|
|
1999
2139
|
prompt: result.usage?.input_tokens || 0,
|
|
2000
2140
|
completion: result.usage?.output_tokens || 0,
|
|
2001
2141
|
reasoning: result.usage?.output_tokens_details?.reasoning_tokens || 0,
|
|
2002
|
-
total: result.usage?.total_tokens || 0
|
|
2142
|
+
total: result.usage?.total_tokens || 0,
|
|
2143
|
+
cached: result.usage?.input_tokens_details?.cached_tokens || 0
|
|
2003
2144
|
}, cost = calculateCost(tokensUsed, model);
|
|
2004
2145
|
let content = "";
|
|
2005
2146
|
if (result.output && Array.isArray(result.output)) {
|
|
@@ -2044,7 +2185,8 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
|
|
|
2044
2185
|
prompt: usage.prompt_tokens || 0,
|
|
2045
2186
|
completion: usage.completion_tokens || 0,
|
|
2046
2187
|
reasoning: usage.reasoning_tokens || 0,
|
|
2047
|
-
total: usage.total_tokens || 0
|
|
2188
|
+
total: usage.total_tokens || 0,
|
|
2189
|
+
cached: usage.prompt_tokens_details?.cached_tokens || 0
|
|
2048
2190
|
}, cost = calculateCost(tokensUsed, model);
|
|
2049
2191
|
return {
|
|
2050
2192
|
content: result.choices[0].message.content || "",
|
|
@@ -2054,47 +2196,47 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
|
|
|
2054
2196
|
}, calculateCost = (tokens, model) => {
|
|
2055
2197
|
const pricing = {
|
|
2056
2198
|
"gpt-5": {
|
|
2057
|
-
input: 125e-
|
|
2058
|
-
output:
|
|
2199
|
+
input: 125e-8,
|
|
2200
|
+
output: 1e-5,
|
|
2059
2201
|
reasoning: 0
|
|
2060
2202
|
},
|
|
2061
2203
|
"gpt-5.1": {
|
|
2062
|
-
input: 125e-
|
|
2063
|
-
output:
|
|
2204
|
+
input: 125e-8,
|
|
2205
|
+
output: 1e-5,
|
|
2064
2206
|
reasoning: 0
|
|
2065
2207
|
},
|
|
2066
2208
|
"gpt-5-mini": {
|
|
2067
|
-
input: 25e-
|
|
2068
|
-
output: 2e-
|
|
2209
|
+
input: 25e-8,
|
|
2210
|
+
output: 2e-6,
|
|
2069
2211
|
reasoning: 0
|
|
2070
2212
|
},
|
|
2071
2213
|
"gpt-5-nano": {
|
|
2072
|
-
input:
|
|
2073
|
-
output:
|
|
2214
|
+
input: 50000000000000004e-24,
|
|
2215
|
+
output: 40000000000000003e-23,
|
|
2074
2216
|
reasoning: 0
|
|
2075
2217
|
},
|
|
2076
2218
|
"gpt-5-pro": {
|
|
2077
|
-
input:
|
|
2078
|
-
output:
|
|
2219
|
+
input: 15e-6,
|
|
2220
|
+
output: 12e-5,
|
|
2079
2221
|
reasoning: 0
|
|
2080
2222
|
},
|
|
2081
2223
|
"gpt-4o": {
|
|
2082
|
-
input:
|
|
2083
|
-
output:
|
|
2224
|
+
input: 25e-7,
|
|
2225
|
+
output: 1e-5,
|
|
2084
2226
|
reasoning: 0
|
|
2085
2227
|
},
|
|
2086
2228
|
"gpt-4-turbo": {
|
|
2087
2229
|
input: 1e-5,
|
|
2088
|
-
output:
|
|
2230
|
+
output: 3e-5,
|
|
2089
2231
|
reasoning: 0
|
|
2090
2232
|
},
|
|
2091
2233
|
"gpt-4": {
|
|
2092
|
-
input:
|
|
2093
|
-
output:
|
|
2234
|
+
input: 3e-5,
|
|
2235
|
+
output: 6e-5,
|
|
2094
2236
|
reasoning: 0
|
|
2095
2237
|
}
|
|
2096
|
-
}, modelPricing = pricing[model] || pricing["gpt-5"];
|
|
2097
|
-
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;
|
|
2098
2240
|
}, deserializeDocument = (originalDoc, reviewedText) => {
|
|
2099
2241
|
const parsed = parseReviewFormat(reviewedText), result = cloneDeep(originalDoc);
|
|
2100
2242
|
return applyTextChanges(result, parsed), result;
|
|
@@ -2245,7 +2387,10 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
|
|
|
2245
2387
|
});
|
|
2246
2388
|
return;
|
|
2247
2389
|
}
|
|
2248
|
-
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
|
+
}));
|
|
2249
2394
|
}, applyBlockChanges = (block, blockNode) => {
|
|
2250
2395
|
if (blockNode.attributes._key && block._key !== blockNode.attributes._key) {
|
|
2251
2396
|
devLog.warn("Block key mismatch, skipping", {
|
|
@@ -2276,7 +2421,10 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
|
|
|
2276
2421
|
});
|
|
2277
2422
|
return;
|
|
2278
2423
|
}
|
|
2279
|
-
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
|
+
}));
|
|
2280
2428
|
}, applySpanChanges = (span, spanNode) => {
|
|
2281
2429
|
if (spanNode.attributes._key && span._key !== spanNode.attributes._key) {
|
|
2282
2430
|
devLog.warn("Span key mismatch, skipping", {
|
|
@@ -2444,6 +2592,7 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
|
|
|
2444
2592
|
}, getOpenAITranslation = async ({
|
|
2445
2593
|
dispatch,
|
|
2446
2594
|
sourceDocument,
|
|
2595
|
+
originalSourceDocument,
|
|
2447
2596
|
languageFieldName,
|
|
2448
2597
|
translationPrompts,
|
|
2449
2598
|
targetLanguage,
|
|
@@ -2484,9 +2633,9 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
|
|
|
2484
2633
|
responseLength: response.content.length,
|
|
2485
2634
|
responsePreview: response.content.substring(0, 500)
|
|
2486
2635
|
});
|
|
2487
|
-
const result = deserializeDocument(
|
|
2636
|
+
const baseDocument = originalSourceDocument || sourceDocument, result = deserializeDocument(baseDocument, response.content);
|
|
2488
2637
|
result[languageFieldName] = targetLanguage;
|
|
2489
|
-
const validation = validateStructureIntegrity(
|
|
2638
|
+
const validation = validateStructureIntegrity(baseDocument, result);
|
|
2490
2639
|
if (!validation.valid) {
|
|
2491
2640
|
devLog.error("Structure validation failed", {
|
|
2492
2641
|
...logContext,
|
|
@@ -2730,14 +2879,30 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2730
2879
|
translationPrompts,
|
|
2731
2880
|
translationMode,
|
|
2732
2881
|
openaiConfig,
|
|
2733
|
-
releaseId
|
|
2882
|
+
releaseId,
|
|
2883
|
+
excludeFields = []
|
|
2734
2884
|
}) => {
|
|
2735
|
-
devLog.
|
|
2885
|
+
devLog.info("Filtering excluded fields before translation", {
|
|
2886
|
+
documentId: sourceDocument._id,
|
|
2887
|
+
documentType: sourceDocument._type,
|
|
2888
|
+
excludeFields,
|
|
2889
|
+
excludeFieldsLength: excludeFields?.length ?? 0,
|
|
2890
|
+
sourceHasTitle: "title" in sourceDocument,
|
|
2891
|
+
sourceTitleValue: sourceDocument.title
|
|
2892
|
+
});
|
|
2893
|
+
const { filteredDocument, excludedValues } = filterExcludedFields(sourceDocument, excludeFields);
|
|
2894
|
+
devLog.info("After filtering excluded fields", {
|
|
2895
|
+
documentId: sourceDocument._id,
|
|
2896
|
+
filteredHasTitle: "title" in filteredDocument,
|
|
2897
|
+
filteredTitleValue: filteredDocument.title,
|
|
2898
|
+
excludedValuesKeys: Object.keys(excludedValues),
|
|
2899
|
+
excludedValues
|
|
2900
|
+
}), devLog.debug("Applying field transforms before translation", {
|
|
2736
2901
|
documentId: sourceDocument._id,
|
|
2737
2902
|
documentType: sourceDocument._type
|
|
2738
2903
|
});
|
|
2739
2904
|
const { transformedDocument, originalStructure } = applyTransforms({
|
|
2740
|
-
document:
|
|
2905
|
+
document: filteredDocument,
|
|
2741
2906
|
registry: globalTransformRegistry,
|
|
2742
2907
|
schemaType: sourceDocument._type
|
|
2743
2908
|
});
|
|
@@ -2760,6 +2925,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2760
2925
|
setJobStatus(dispatch, sourceDocument._id, targetLanguage, "translating_with_openai"), translatedDoc = await getOpenAITranslation({
|
|
2761
2926
|
dispatch,
|
|
2762
2927
|
sourceDocument: transformedDocument,
|
|
2928
|
+
originalSourceDocument: sourceDocument,
|
|
2763
2929
|
targetLanguage,
|
|
2764
2930
|
languageFieldName,
|
|
2765
2931
|
translationPrompts,
|
|
@@ -2778,12 +2944,26 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2778
2944
|
documentId: sourceDocument._id,
|
|
2779
2945
|
documentType: sourceDocument._type
|
|
2780
2946
|
});
|
|
2781
|
-
const
|
|
2947
|
+
const transformedResult = restoreTransforms({
|
|
2782
2948
|
translatedDocument: translatedDoc,
|
|
2783
2949
|
originalStructure,
|
|
2784
2950
|
registry: globalTransformRegistry,
|
|
2785
2951
|
schemaType: sourceDocument._type
|
|
2786
|
-
})
|
|
2952
|
+
});
|
|
2953
|
+
devLog.info("Before restoring excluded fields", {
|
|
2954
|
+
documentId: sourceDocument._id,
|
|
2955
|
+
excludedFieldCount: Object.keys(excludedValues).length,
|
|
2956
|
+
excludedValuesKeys: Object.keys(excludedValues),
|
|
2957
|
+
transformedResultHasTitle: "title" in transformedResult,
|
|
2958
|
+
transformedResultTitle: transformedResult.title
|
|
2959
|
+
});
|
|
2960
|
+
const finalDoc = restoreExcludedFields(transformedResult, excludedValues);
|
|
2961
|
+
devLog.info("After restoring excluded fields", {
|
|
2962
|
+
documentId: sourceDocument._id,
|
|
2963
|
+
finalDocHasTitle: "title" in finalDoc,
|
|
2964
|
+
finalDocTitle: finalDoc.title
|
|
2965
|
+
});
|
|
2966
|
+
const targetDocumentPublishedId = existingTranslationId || uuid(), targetDocumentVersionId = `versions.${releaseId}.${targetDocumentPublishedId}`, newDoc = {
|
|
2787
2967
|
...finalDoc,
|
|
2788
2968
|
_id: targetDocumentVersionId
|
|
2789
2969
|
};
|
|
@@ -2804,7 +2984,8 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2804
2984
|
translationPrompts,
|
|
2805
2985
|
translationMode,
|
|
2806
2986
|
openaiConfig,
|
|
2807
|
-
releaseId
|
|
2987
|
+
releaseId,
|
|
2988
|
+
excludeFields
|
|
2808
2989
|
}) => {
|
|
2809
2990
|
const logContext = {
|
|
2810
2991
|
documentId: sourceDocument._id,
|
|
@@ -2840,7 +3021,8 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2840
3021
|
translationPrompts,
|
|
2841
3022
|
translationMode,
|
|
2842
3023
|
openaiConfig,
|
|
2843
|
-
releaseId
|
|
3024
|
+
releaseId,
|
|
3025
|
+
excludeFields
|
|
2844
3026
|
}),
|
|
2845
3027
|
{
|
|
2846
3028
|
maxRetries: 3,
|
|
@@ -2906,7 +3088,8 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2906
3088
|
references = [],
|
|
2907
3089
|
maxConcurrentTranslationJobs,
|
|
2908
3090
|
translationMode,
|
|
2909
|
-
openaiConfig
|
|
3091
|
+
openaiConfig,
|
|
3092
|
+
excludeFields
|
|
2910
3093
|
}) => {
|
|
2911
3094
|
const logContext = {
|
|
2912
3095
|
documentId: sourceDocument._id,
|
|
@@ -2972,7 +3155,8 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2972
3155
|
translationPrompts,
|
|
2973
3156
|
translationMode,
|
|
2974
3157
|
openaiConfig,
|
|
2975
|
-
releaseId: releaseId2
|
|
3158
|
+
releaseId: releaseId2,
|
|
3159
|
+
excludeFields
|
|
2976
3160
|
});
|
|
2977
3161
|
versionedTargetDocumentId && versionedTargetDocumentIds.push(versionedTargetDocumentId);
|
|
2978
3162
|
}
|
|
@@ -3045,7 +3229,8 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3045
3229
|
maxConcurrentTranslationJobs: config.maxConcurrentTranslationJobs,
|
|
3046
3230
|
maxReferenceDepth: config.maxReferenceDepth,
|
|
3047
3231
|
translationMode,
|
|
3048
|
-
openaiConfig: config.openai
|
|
3232
|
+
openaiConfig: config.openai,
|
|
3233
|
+
excludeFields: config.excludeFields
|
|
3049
3234
|
});
|
|
3050
3235
|
}, [
|
|
3051
3236
|
document,
|
|
@@ -3057,6 +3242,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3057
3242
|
config.maxConcurrentTranslationJobs,
|
|
3058
3243
|
config.maxReferenceDepth,
|
|
3059
3244
|
config.openai,
|
|
3245
|
+
config.excludeFields,
|
|
3060
3246
|
references,
|
|
3061
3247
|
shouldOverwriteFreshTranslations,
|
|
3062
3248
|
translationMode
|
|
@@ -3439,7 +3625,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3439
3625
|
/* @__PURE__ */ jsxs(Code, { size: 1, children: [
|
|
3440
3626
|
job._type,
|
|
3441
3627
|
" \u2014 ",
|
|
3442
|
-
job._id
|
|
3628
|
+
job._id?.slice(-6) || "unknown"
|
|
3443
3629
|
] }),
|
|
3444
3630
|
!["completed", "skipped", "error"].includes(job.status) && /* @__PURE__ */ jsx(Text, { size: 1, weight: "semibold", children: getStatusLabel(job.status) }),
|
|
3445
3631
|
job.status === "completed" && job.tokensUsed ? /* @__PURE__ */ jsxs(Text, { size: 1, muted: !0, children: [
|
|
@@ -4038,7 +4224,11 @@ const aiTranslationPlugin = definePlugin((config) => (config.fieldTransforms?.le
|
|
|
4038
4224
|
}
|
|
4039
4225
|
})), translationHistorySchema = translationHistory;
|
|
4040
4226
|
export {
|
|
4227
|
+
COMMON_ENUM_FIELDS,
|
|
4228
|
+
PREDEFINED_EXCLUSIONS,
|
|
4041
4229
|
aiTranslationPlugin,
|
|
4230
|
+
filterExcludedFields,
|
|
4231
|
+
restoreExcludedFields,
|
|
4042
4232
|
richTextTransform,
|
|
4043
4233
|
translationHistorySchema,
|
|
4044
4234
|
vttCaptionTransform
|