@verbatra/sdk 0.5.0-next.2 → 0.5.0-next.3
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.cjs +196 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +196 -17
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -807,6 +807,50 @@ function createDefaultClient2() {
|
|
|
807
807
|
};
|
|
808
808
|
return { client, freeAccount };
|
|
809
809
|
}
|
|
810
|
+
var DEEPL_MAX_TEXTS_PER_REQUEST = 50;
|
|
811
|
+
var DEEPL_MAX_PAYLOAD_BYTES = 128 * 1024;
|
|
812
|
+
var DEEPL_PAYLOAD_OVERHEAD_RESERVE_BYTES = 4 * 1024;
|
|
813
|
+
var DEEPL_MAX_TEXT_PAYLOAD_BYTES = DEEPL_MAX_PAYLOAD_BYTES - DEEPL_PAYLOAD_OVERHEAD_RESERVE_BYTES;
|
|
814
|
+
function estimateWireBytes(text) {
|
|
815
|
+
return encodeURIComponent(text).length;
|
|
816
|
+
}
|
|
817
|
+
function chunkTextsForDeepL(texts) {
|
|
818
|
+
const chunks = [];
|
|
819
|
+
let current = [];
|
|
820
|
+
let currentBytes = 0;
|
|
821
|
+
for (const text of texts) {
|
|
822
|
+
const textBytes = estimateWireBytes(text);
|
|
823
|
+
const startsNewChunk = current.length > 0 && (current.length >= DEEPL_MAX_TEXTS_PER_REQUEST || currentBytes + textBytes > DEEPL_MAX_TEXT_PAYLOAD_BYTES);
|
|
824
|
+
if (startsNewChunk) {
|
|
825
|
+
chunks.push(current);
|
|
826
|
+
current = [];
|
|
827
|
+
currentBytes = 0;
|
|
828
|
+
}
|
|
829
|
+
current.push(text);
|
|
830
|
+
currentBytes += textBytes;
|
|
831
|
+
}
|
|
832
|
+
if (current.length > 0) {
|
|
833
|
+
chunks.push(current);
|
|
834
|
+
}
|
|
835
|
+
return chunks;
|
|
836
|
+
}
|
|
837
|
+
var DEPRECATED_BARE_TARGET_CODES = /* @__PURE__ */ new Set(["en", "pt"]);
|
|
838
|
+
function assertValidDeepLSourceLocale(locale) {
|
|
839
|
+
if (locale.includes("-")) {
|
|
840
|
+
throw new ProviderError(
|
|
841
|
+
"INVALID_REQUEST",
|
|
842
|
+
`DeepL does not accept a regional or script source locale code: "${locale}". Only the base language code is valid as a DeepL source (for example, use "en" instead of "en-US").`
|
|
843
|
+
);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
function assertValidDeepLTargetLocale(locale) {
|
|
847
|
+
if (DEPRECATED_BARE_TARGET_CODES.has(locale.toLowerCase())) {
|
|
848
|
+
throw new ProviderError(
|
|
849
|
+
"INVALID_REQUEST",
|
|
850
|
+
`DeepL requires a disambiguated target locale code instead of "${locale}" (for example, "en-GB" or "en-US" for English, "pt-PT" or "pt-BR" for Portuguese).`
|
|
851
|
+
);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
810
854
|
var PLACEHOLDER_UNSUPPORTED_MESSAGE = "Some entries contain placeholders or ICU syntax that DeepL cannot preserve; they were left untranslated. Use an LLM provider to translate placeholder-bearing strings.";
|
|
811
855
|
function partitionByPlaceholders(entries) {
|
|
812
856
|
const protectable = [];
|
|
@@ -886,6 +930,8 @@ function resolveClient(deps) {
|
|
|
886
930
|
}
|
|
887
931
|
async function translate(bundle, config, request) {
|
|
888
932
|
const data = validateRequest(request);
|
|
933
|
+
assertValidDeepLSourceLocale(data.sourceLocale);
|
|
934
|
+
assertValidDeepLTargetLocale(data.targetLocale);
|
|
889
935
|
const { protectable, unprotectable } = partitionByPlaceholders(data.entries);
|
|
890
936
|
const genericGlossarySupplied = request.glossary !== void 0 && Object.keys(request.glossary).length > 0;
|
|
891
937
|
const { options, notices } = buildTranslateOptions({
|
|
@@ -913,7 +959,7 @@ async function translateProtectable(client, data, protectable, options, extract,
|
|
|
913
959
|
return { values: /* @__PURE__ */ new Map(), integrity: /* @__PURE__ */ new Map() };
|
|
914
960
|
}
|
|
915
961
|
const texts = protectable.map((entry) => entry.value);
|
|
916
|
-
const results = await
|
|
962
|
+
const results = await callClientChunked(
|
|
917
963
|
client,
|
|
918
964
|
texts,
|
|
919
965
|
data.sourceLocale,
|
|
@@ -931,6 +977,14 @@ function callClient2(client, texts, sourceLang, targetLang, options, signal) {
|
|
|
931
977
|
signal
|
|
932
978
|
);
|
|
933
979
|
}
|
|
980
|
+
async function callClientChunked(client, texts, sourceLang, targetLang, options, signal) {
|
|
981
|
+
const results = [];
|
|
982
|
+
for (const chunk2 of chunkTextsForDeepL(texts)) {
|
|
983
|
+
const chunkResults = await callClient2(client, chunk2, sourceLang, targetLang, options, signal);
|
|
984
|
+
results.push(...chunkResults);
|
|
985
|
+
}
|
|
986
|
+
return results;
|
|
987
|
+
}
|
|
934
988
|
var geminiConfigSchema = zod.z.object({
|
|
935
989
|
model: zod.z.string().min(1),
|
|
936
990
|
maxOutputTokens: zod.z.number().int().positive()
|
|
@@ -1402,6 +1456,17 @@ function buildProvider(config) {
|
|
|
1402
1456
|
|
|
1403
1457
|
// src/config/schema.ts
|
|
1404
1458
|
var DEFAULT_MAX_BATCH_SIZE = 50;
|
|
1459
|
+
function findCaseInsensitiveDuplicate(locales) {
|
|
1460
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1461
|
+
for (const locale of locales) {
|
|
1462
|
+
const key = locale.toLowerCase();
|
|
1463
|
+
if (seen.has(key)) {
|
|
1464
|
+
return locale;
|
|
1465
|
+
}
|
|
1466
|
+
seen.add(key);
|
|
1467
|
+
}
|
|
1468
|
+
return void 0;
|
|
1469
|
+
}
|
|
1405
1470
|
var verbatraConfigSchema = zod.z.strictObject({
|
|
1406
1471
|
sourceLocale: zod.z.string().min(1),
|
|
1407
1472
|
targetLocales: zod.z.array(zod.z.string().min(1)).min(1),
|
|
@@ -1443,6 +1508,14 @@ var verbatraConfigSchema = zod.z.strictObject({
|
|
|
1443
1508
|
}).refine((config) => !config.targetLocales.includes(config.sourceLocale), {
|
|
1444
1509
|
message: "targetLocales must not include the source locale",
|
|
1445
1510
|
path: ["targetLocales"]
|
|
1511
|
+
}).refine((config) => findCaseInsensitiveDuplicate(config.targetLocales) === void 0, {
|
|
1512
|
+
error: (issue) => {
|
|
1513
|
+
const duplicate = findCaseInsensitiveDuplicate(
|
|
1514
|
+
issue.input.targetLocales
|
|
1515
|
+
);
|
|
1516
|
+
return `targetLocales must not contain case-insensitively duplicate locales: "${duplicate}"`;
|
|
1517
|
+
},
|
|
1518
|
+
path: ["targetLocales"]
|
|
1446
1519
|
}).refine((config) => config.files.pattern.includes(LOCALE_TOKEN), {
|
|
1447
1520
|
message: `files.pattern must contain the ${LOCALE_TOKEN} token`,
|
|
1448
1521
|
path: ["files", "pattern"]
|
|
@@ -2158,11 +2231,24 @@ function unflattenEntries(entries) {
|
|
|
2158
2231
|
}
|
|
2159
2232
|
return root;
|
|
2160
2233
|
}
|
|
2161
|
-
function
|
|
2234
|
+
function mergeDescriptions(entries, content, deriveDescriptions) {
|
|
2235
|
+
if (!deriveDescriptions) {
|
|
2236
|
+
return;
|
|
2237
|
+
}
|
|
2238
|
+
for (const [key, description] of deriveDescriptions(content)) {
|
|
2239
|
+
const entry = entries.get(key);
|
|
2240
|
+
if (entry !== void 0) {
|
|
2241
|
+
entries.set(key, { ...entry, description });
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
function toEntries(content, namespace, parse3, deriveEntry, keyMode, validateTree, deriveDescriptions) {
|
|
2162
2246
|
try {
|
|
2163
2247
|
const tree = parse3(content);
|
|
2164
2248
|
validateTree?.(tree);
|
|
2165
|
-
|
|
2249
|
+
const entries = flattenTree(tree, namespace, deriveEntry, keyMode);
|
|
2250
|
+
mergeDescriptions(entries, content, deriveDescriptions);
|
|
2251
|
+
return entries;
|
|
2166
2252
|
} catch (error) {
|
|
2167
2253
|
rethrowStructured(error, "The file could not be parsed.");
|
|
2168
2254
|
}
|
|
@@ -2181,6 +2267,7 @@ function createTreeFileAdapter(options) {
|
|
|
2181
2267
|
validateTree,
|
|
2182
2268
|
buildWriteTree,
|
|
2183
2269
|
comparePlaceholders,
|
|
2270
|
+
deriveDescriptions,
|
|
2184
2271
|
keyMode = "literal-leaf"
|
|
2185
2272
|
} = options;
|
|
2186
2273
|
return {
|
|
@@ -2192,7 +2279,15 @@ function createTreeFileAdapter(options) {
|
|
|
2192
2279
|
async read(filePath, locale) {
|
|
2193
2280
|
const content = await readFileContent(filePath);
|
|
2194
2281
|
const namespace = namespaceOf(filePath);
|
|
2195
|
-
const entries = toEntries(
|
|
2282
|
+
const entries = toEntries(
|
|
2283
|
+
content,
|
|
2284
|
+
namespace,
|
|
2285
|
+
parse3,
|
|
2286
|
+
deriveEntry,
|
|
2287
|
+
keyMode,
|
|
2288
|
+
validateTree,
|
|
2289
|
+
deriveDescriptions
|
|
2290
|
+
);
|
|
2196
2291
|
const resource = { locale, namespace, format, entries };
|
|
2197
2292
|
const invalidIcuKeys = computeIcu(entries, computeInvalidIcuKeys);
|
|
2198
2293
|
return { resource, invalidIcuKeys };
|
|
@@ -2233,6 +2328,31 @@ function stripArbMetadata(tree) {
|
|
|
2233
2328
|
function originalKey(encoded) {
|
|
2234
2329
|
return decodeKeyToSegments(encoded).join(".");
|
|
2235
2330
|
}
|
|
2331
|
+
function messageKeyForMetadata(key) {
|
|
2332
|
+
return isMetadataKey(key) && !key.startsWith("@@") ? key.slice(1) : null;
|
|
2333
|
+
}
|
|
2334
|
+
function descriptionOf(value) {
|
|
2335
|
+
if (typeof value !== "object" || value === null) {
|
|
2336
|
+
return void 0;
|
|
2337
|
+
}
|
|
2338
|
+
const description = value.description;
|
|
2339
|
+
return typeof description === "string" ? description : void 0;
|
|
2340
|
+
}
|
|
2341
|
+
function extractArbDescriptions(content) {
|
|
2342
|
+
const tree = parseArbObject(content);
|
|
2343
|
+
const out = /* @__PURE__ */ new Map();
|
|
2344
|
+
for (const [key, value] of Object.entries(tree)) {
|
|
2345
|
+
const messageKey = messageKeyForMetadata(key);
|
|
2346
|
+
if (messageKey === null) {
|
|
2347
|
+
continue;
|
|
2348
|
+
}
|
|
2349
|
+
const description = descriptionOf(value);
|
|
2350
|
+
if (description !== void 0) {
|
|
2351
|
+
out.set(encodeSegment(messageKey), description);
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
return out;
|
|
2355
|
+
}
|
|
2236
2356
|
function messagesFromEntries(entries) {
|
|
2237
2357
|
const out = /* @__PURE__ */ new Map();
|
|
2238
2358
|
for (const [key, entry] of entries) {
|
|
@@ -2289,7 +2409,8 @@ function createArbAdapter() {
|
|
|
2289
2409
|
computeInvalidIcuKeys: icuInvalidKeys,
|
|
2290
2410
|
validateMessage: icuIsValid,
|
|
2291
2411
|
comparePlaceholders: compareIcuPlaceholders,
|
|
2292
|
-
buildWriteTree: buildArbWriteTree
|
|
2412
|
+
buildWriteTree: buildArbWriteTree,
|
|
2413
|
+
deriveDescriptions: extractArbDescriptions
|
|
2293
2414
|
});
|
|
2294
2415
|
}
|
|
2295
2416
|
function createJsonFileAdapter(options) {
|
|
@@ -2595,16 +2716,33 @@ function parseXml(content) {
|
|
|
2595
2716
|
}
|
|
2596
2717
|
return { doc, root };
|
|
2597
2718
|
}
|
|
2719
|
+
function firstNoteText(parent) {
|
|
2720
|
+
const note = childByName(parent, "note");
|
|
2721
|
+
if (note === null) {
|
|
2722
|
+
return void 0;
|
|
2723
|
+
}
|
|
2724
|
+
const text = (note.textContent ?? "").trim();
|
|
2725
|
+
return text === "" ? void 0 : text;
|
|
2726
|
+
}
|
|
2727
|
+
function transUnitDescription(tu) {
|
|
2728
|
+
return firstNoteText(tu);
|
|
2729
|
+
}
|
|
2730
|
+
function unitDescription(unit) {
|
|
2731
|
+
const notes = childByName(unit, "notes");
|
|
2732
|
+
return notes === null ? void 0 : firstNoteText(notes);
|
|
2733
|
+
}
|
|
2598
2734
|
function walkXliff12(root) {
|
|
2599
2735
|
const units = [];
|
|
2600
2736
|
collectByTag(root, "trans-unit").forEach((tu, index) => {
|
|
2601
2737
|
const source = childByName(tu, "source");
|
|
2602
2738
|
if (source !== null) {
|
|
2739
|
+
const description = transUnitDescription(tu);
|
|
2603
2740
|
units.push({
|
|
2604
2741
|
key: unitKey(tu, index),
|
|
2605
2742
|
source,
|
|
2606
2743
|
target: childByName(tu, "target"),
|
|
2607
|
-
container: tu
|
|
2744
|
+
container: tu,
|
|
2745
|
+
...description !== void 0 ? { description } : {}
|
|
2608
2746
|
});
|
|
2609
2747
|
}
|
|
2610
2748
|
});
|
|
@@ -2614,12 +2752,19 @@ function walkXliff20(root) {
|
|
|
2614
2752
|
const units = [];
|
|
2615
2753
|
collectByTag(root, "unit").forEach((unit, index) => {
|
|
2616
2754
|
const baseKey = unitKey(unit, index);
|
|
2755
|
+
const description = unitDescription(unit);
|
|
2617
2756
|
const segments = elementChildren(unit).filter((el) => el.localName === "segment");
|
|
2618
2757
|
segments.forEach((segment, segIndex) => {
|
|
2619
2758
|
const source = childByName(segment, "source");
|
|
2620
2759
|
if (source !== null) {
|
|
2621
2760
|
const key = segments.length > 1 ? `${baseKey}#${segIndex}` : baseKey;
|
|
2622
|
-
units.push({
|
|
2761
|
+
units.push({
|
|
2762
|
+
key,
|
|
2763
|
+
source,
|
|
2764
|
+
target: childByName(segment, "target"),
|
|
2765
|
+
container: segment,
|
|
2766
|
+
...description !== void 0 ? { description } : {}
|
|
2767
|
+
});
|
|
2623
2768
|
}
|
|
2624
2769
|
});
|
|
2625
2770
|
});
|
|
@@ -2658,7 +2803,8 @@ function parseXliffEntries(content, namespace) {
|
|
|
2658
2803
|
namespace,
|
|
2659
2804
|
value,
|
|
2660
2805
|
placeholders: extractXliffPlaceholders(value),
|
|
2661
|
-
isPlural: false
|
|
2806
|
+
isPlural: false,
|
|
2807
|
+
...unit.description !== void 0 ? { description: unit.description } : {}
|
|
2662
2808
|
});
|
|
2663
2809
|
}
|
|
2664
2810
|
return out;
|
|
@@ -3549,10 +3695,13 @@ var INSTRUCTIONS_LINES = [
|
|
|
3549
3695
|
" structure and the argument names exactly. Translate only the human-readable text.",
|
|
3550
3696
|
"6. Do not edit the 'Key' column or the hidden 'Source hash' column. They map your translation",
|
|
3551
3697
|
" back to the right string. You may sort or filter rows freely; mapping does not depend on row order.",
|
|
3698
|
+
"7. The 'Context' column, when present, is reference text from the source file explaining what the",
|
|
3699
|
+
" string means or where it is used. It is read-only; leave it as-is.",
|
|
3552
3700
|
"",
|
|
3553
3701
|
"Status values:",
|
|
3554
3702
|
" new - this string has no translation yet.",
|
|
3555
3703
|
" changed - the source string changed and the translation needs updating.",
|
|
3704
|
+
" unchanged - already up to date, included for reference. No action needed.",
|
|
3556
3705
|
"",
|
|
3557
3706
|
"When you are done, save the file and send it back. verbatra checks every value on import:",
|
|
3558
3707
|
"a value with a broken placeholder, invalid ICU, or a source that changed since export is",
|
|
@@ -3564,7 +3713,8 @@ var COLUMN = {
|
|
|
3564
3713
|
current: 3,
|
|
3565
3714
|
status: 4,
|
|
3566
3715
|
translation: 5,
|
|
3567
|
-
sourceHash: 6
|
|
3716
|
+
sourceHash: 6,
|
|
3717
|
+
context: 7
|
|
3568
3718
|
};
|
|
3569
3719
|
var HEADERS = [
|
|
3570
3720
|
"Key",
|
|
@@ -3572,7 +3722,8 @@ var HEADERS = [
|
|
|
3572
3722
|
"Current translation",
|
|
3573
3723
|
"Status",
|
|
3574
3724
|
"Translation",
|
|
3575
|
-
"Source hash"
|
|
3725
|
+
"Source hash",
|
|
3726
|
+
"Context"
|
|
3576
3727
|
];
|
|
3577
3728
|
var HEADER_ROW = 1;
|
|
3578
3729
|
var INSTRUCTIONS_SHEET_NAME = "Instructions";
|
|
@@ -3591,7 +3742,8 @@ var COLUMN_WIDTHS = {
|
|
|
3591
3742
|
[COLUMN.source]: 50,
|
|
3592
3743
|
[COLUMN.current]: 50,
|
|
3593
3744
|
[COLUMN.status]: 12,
|
|
3594
|
-
[COLUMN.translation]: 50
|
|
3745
|
+
[COLUMN.translation]: 50,
|
|
3746
|
+
[COLUMN.context]: 50
|
|
3595
3747
|
};
|
|
3596
3748
|
var TEXT_NUMBER_FORMAT = "@";
|
|
3597
3749
|
function styleHeader(sheet) {
|
|
@@ -3622,7 +3774,8 @@ function writeRow(sheet, sheetRow) {
|
|
|
3622
3774
|
translationCell.numFmt = TEXT_NUMBER_FORMAT;
|
|
3623
3775
|
translationCell.value = sheetRow.translation === "" ? null : sheetRow.translation;
|
|
3624
3776
|
row.getCell(COLUMN.sourceHash).value = sheetRow.sourceHash;
|
|
3625
|
-
|
|
3777
|
+
row.getCell(COLUMN.context).value = sheetRow.context;
|
|
3778
|
+
for (let column = COLUMN.key; column <= COLUMN.context; column += 1) {
|
|
3626
3779
|
const cell = row.getCell(column);
|
|
3627
3780
|
cell.protection = { locked: column !== COLUMN.translation };
|
|
3628
3781
|
if (column !== COLUMN.translation) {
|
|
@@ -3647,6 +3800,26 @@ function assertValidWorksheetName(locale) {
|
|
|
3647
3800
|
);
|
|
3648
3801
|
}
|
|
3649
3802
|
}
|
|
3803
|
+
function assertNoWorksheetNameCollisions(sheets) {
|
|
3804
|
+
const reservedKey = INSTRUCTIONS_SHEET_NAME.toLowerCase();
|
|
3805
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3806
|
+
for (const sheet of sheets) {
|
|
3807
|
+
const key = sheet.locale.toLowerCase();
|
|
3808
|
+
if (key === reservedKey) {
|
|
3809
|
+
throw new ExchangeError(
|
|
3810
|
+
"WORKBOOK_INVALID",
|
|
3811
|
+
`The locale "${sheet.locale}" cannot be an Excel worksheet name: it collides with the reserved "${INSTRUCTIONS_SHEET_NAME}" sheet.`
|
|
3812
|
+
);
|
|
3813
|
+
}
|
|
3814
|
+
if (seen.has(key)) {
|
|
3815
|
+
throw new ExchangeError(
|
|
3816
|
+
"WORKBOOK_INVALID",
|
|
3817
|
+
`The locale "${sheet.locale}" cannot be an Excel worksheet name: it collides with another target locale's sheet name.`
|
|
3818
|
+
);
|
|
3819
|
+
}
|
|
3820
|
+
seen.add(key);
|
|
3821
|
+
}
|
|
3822
|
+
}
|
|
3650
3823
|
async function buildDataSheet(workbook, sheet) {
|
|
3651
3824
|
assertValidWorksheetName(sheet.locale);
|
|
3652
3825
|
const worksheet = workbook.addWorksheet(sheet.locale);
|
|
@@ -3673,6 +3846,7 @@ function buildInstructionsSheet(workbook) {
|
|
|
3673
3846
|
sheet.getRow(1).font = { bold: true };
|
|
3674
3847
|
}
|
|
3675
3848
|
async function buildWorkbook(model) {
|
|
3849
|
+
assertNoWorksheetNameCollisions(model.sheets);
|
|
3676
3850
|
const workbook = new ExcelJS__default.default.Workbook();
|
|
3677
3851
|
buildInstructionsSheet(workbook);
|
|
3678
3852
|
for (const sheet of model.sheets) {
|
|
@@ -3773,9 +3947,10 @@ var rowSchema = zod.z.object({
|
|
|
3773
3947
|
key: zod.z.string().min(1),
|
|
3774
3948
|
source: zod.z.string(),
|
|
3775
3949
|
currentTarget: zod.z.string(),
|
|
3776
|
-
status: zod.z.enum(["new", "changed"]),
|
|
3950
|
+
status: zod.z.enum(["new", "changed", "unchanged"]),
|
|
3777
3951
|
sourceHash: zod.z.string(),
|
|
3778
|
-
translation: zod.z.string()
|
|
3952
|
+
translation: zod.z.string(),
|
|
3953
|
+
context: zod.z.string()
|
|
3779
3954
|
});
|
|
3780
3955
|
function cellString(cell) {
|
|
3781
3956
|
const value = cell.value;
|
|
@@ -3808,7 +3983,10 @@ function parseRow(sheet, row) {
|
|
|
3808
3983
|
currentTarget: cellString(row.getCell(COLUMN.current)),
|
|
3809
3984
|
status: cellString(row.getCell(COLUMN.status)),
|
|
3810
3985
|
sourceHash: cellString(row.getCell(COLUMN.sourceHash)),
|
|
3811
|
-
translation: cellString(row.getCell(COLUMN.translation))
|
|
3986
|
+
translation: cellString(row.getCell(COLUMN.translation)),
|
|
3987
|
+
// A workbook built before the Context column existed has no cell here; getCell auto-vivifies an
|
|
3988
|
+
// empty one, so cellString yields "" and the row still validates, keeping import backward-compatible.
|
|
3989
|
+
context: cellString(row.getCell(COLUMN.context))
|
|
3812
3990
|
};
|
|
3813
3991
|
const result = rowSchema.safeParse(candidate);
|
|
3814
3992
|
if (!result.success) {
|
|
@@ -3897,14 +4075,15 @@ function buildRows(source, target, baseline, includeUnchanged) {
|
|
|
3897
4075
|
currentTarget: target.entries.get(key)?.value ?? "",
|
|
3898
4076
|
status,
|
|
3899
4077
|
sourceHash: contentHash(sourceEntry),
|
|
3900
|
-
translation: ""
|
|
4078
|
+
translation: "",
|
|
4079
|
+
context: sourceEntry.description ?? ""
|
|
3901
4080
|
});
|
|
3902
4081
|
}
|
|
3903
4082
|
};
|
|
3904
4083
|
add(diff2.missing, "new");
|
|
3905
4084
|
add(diff2.changed, "changed");
|
|
3906
4085
|
if (includeUnchanged) {
|
|
3907
|
-
add(diff2.unchanged, "
|
|
4086
|
+
add(diff2.unchanged, "unchanged");
|
|
3908
4087
|
}
|
|
3909
4088
|
return [...rows].sort((a, b) => a.key < b.key ? -1 : 1);
|
|
3910
4089
|
}
|