@verbatra/sdk 0.4.3 → 0.4.4
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/README.md +1 -1
- package/dist/index.cjs +199 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -2
- package/dist/index.d.ts +14 -2
- package/dist/index.js +199 -67
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -86,7 +86,7 @@ Discovers and validates the configuration. With no arguments it searches upward
|
|
|
86
86
|
|
|
87
87
|
### `translate(input): Promise<RunSummary>`
|
|
88
88
|
|
|
89
|
-
Runs the one-shot read, diff, translate, write flow over every target locale. `input` is `{ config, cwd?, dryRun?, prune?, generatePlurals? }`. With `dryRun: true` it reads, diffs, and reports without calling the provider or writing anything. `prune` and `generatePlurals` each override the matching config option for this run. Resolves to a `RunSummary` (`dryRun`, `locales`, `succeeded`, and `failed`); each locale summary lists `translated`, `generated`, `pruned`, `integrityMismatches`, and `notices`.
|
|
89
|
+
Runs the one-shot read, diff, translate, write flow over every target locale. `input` is `{ config, cwd?, dryRun?, prune?, generatePlurals? }`. With `dryRun: true` it reads, diffs, and reports without calling the provider or writing anything. `prune` and `generatePlurals` each override the matching config option for this run. Resolves to a `RunSummary` (`dryRun`, `locales`, `succeeded`, and `failed`); each locale summary lists `translated`, `generated`, `pruned`, `integrityMismatches`, `providerFailures`, and `notices`. `integrityMismatches` is a translation that came back and failed the placeholder-integrity check; `providerFailures` is a key withheld because the provider call itself failed (nothing was translated), with the secret-free failure code and message reported in `notices`.
|
|
90
90
|
|
|
91
91
|
```ts
|
|
92
92
|
const preview = await translate({ config, dryRun: true });
|
package/dist/index.cjs
CHANGED
|
@@ -1182,19 +1182,53 @@ function childMessages(element) {
|
|
|
1182
1182
|
}
|
|
1183
1183
|
return [];
|
|
1184
1184
|
}
|
|
1185
|
-
function
|
|
1185
|
+
function addCount(target, token, count) {
|
|
1186
|
+
target.set(token, (target.get(token) ?? 0) + count);
|
|
1187
|
+
}
|
|
1188
|
+
function combineBranches(branchMultisets) {
|
|
1189
|
+
const combined = /* @__PURE__ */ new Map();
|
|
1190
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
1191
|
+
for (const branch of branchMultisets) {
|
|
1192
|
+
for (const token of branch.keys()) {
|
|
1193
|
+
tokens.add(token);
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
for (const token of tokens) {
|
|
1197
|
+
const minCount = Math.min(...branchMultisets.map((branch) => branch.get(token) ?? 0));
|
|
1198
|
+
if (minCount > 0) {
|
|
1199
|
+
combined.set(token, minCount);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
return combined;
|
|
1203
|
+
}
|
|
1204
|
+
function collect(elements, state) {
|
|
1205
|
+
const result = /* @__PURE__ */ new Map();
|
|
1186
1206
|
for (const element of elements) {
|
|
1187
1207
|
const token = tokenOf(element);
|
|
1188
1208
|
if (token !== void 0) {
|
|
1189
|
-
|
|
1209
|
+
addCount(result, token, 1);
|
|
1190
1210
|
}
|
|
1191
1211
|
if (element.type === icuMessageformatParser.TYPE.plural) {
|
|
1192
1212
|
state.isPlural = true;
|
|
1193
1213
|
}
|
|
1194
|
-
|
|
1195
|
-
|
|
1214
|
+
const branches = childMessages(element);
|
|
1215
|
+
if (branches.length > 0) {
|
|
1216
|
+
const branchMultisets = branches.map((branch) => collect(branch, state));
|
|
1217
|
+
for (const [branchToken, branchCount] of combineBranches(branchMultisets)) {
|
|
1218
|
+
addCount(result, branchToken, branchCount);
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
return result;
|
|
1223
|
+
}
|
|
1224
|
+
function flatten(multiset) {
|
|
1225
|
+
const flattened = [];
|
|
1226
|
+
for (const [token, count] of multiset) {
|
|
1227
|
+
for (let i = 0; i < count; i += 1) {
|
|
1228
|
+
flattened.push(token);
|
|
1196
1229
|
}
|
|
1197
1230
|
}
|
|
1231
|
+
return flattened;
|
|
1198
1232
|
}
|
|
1199
1233
|
function analyzeIcuValue(value) {
|
|
1200
1234
|
if (!value.includes("{") && !value.includes("<")) {
|
|
@@ -1202,16 +1236,9 @@ function analyzeIcuValue(value) {
|
|
|
1202
1236
|
}
|
|
1203
1237
|
try {
|
|
1204
1238
|
const ast = icuMessageformatParser.parse(value);
|
|
1205
|
-
const placeholders = [];
|
|
1206
1239
|
const state = { isPlural: false };
|
|
1207
|
-
collect(
|
|
1208
|
-
|
|
1209
|
-
(token) => {
|
|
1210
|
-
placeholders.push(token);
|
|
1211
|
-
},
|
|
1212
|
-
state
|
|
1213
|
-
);
|
|
1214
|
-
return { placeholders, isPlural: state.isPlural, valid: true };
|
|
1240
|
+
const multiset = collect(ast, state);
|
|
1241
|
+
return { placeholders: flatten(multiset), isPlural: state.isPlural, valid: true };
|
|
1215
1242
|
} catch {
|
|
1216
1243
|
return INVALID;
|
|
1217
1244
|
}
|
|
@@ -1473,21 +1500,28 @@ function addEntries(ctx, prefix, node) {
|
|
|
1473
1500
|
}
|
|
1474
1501
|
}
|
|
1475
1502
|
}
|
|
1476
|
-
function addPathEntries(node, prefix, namespace, derive, out) {
|
|
1503
|
+
function addPathEntries(node, prefix, namespace, derive, out, claimedPaths) {
|
|
1477
1504
|
for (const [key, value] of Object.entries(node)) {
|
|
1478
1505
|
const path = prefix === "" ? key : `${prefix}.${key}`;
|
|
1506
|
+
if (claimedPaths.has(path)) {
|
|
1507
|
+
throw new AdapterError(
|
|
1508
|
+
"INVALID_STRUCTURE",
|
|
1509
|
+
"A dotted key and a nested key path resolve to the same path."
|
|
1510
|
+
);
|
|
1511
|
+
}
|
|
1512
|
+
claimedPaths.add(path);
|
|
1479
1513
|
if (typeof value === "string") {
|
|
1480
1514
|
const { placeholders, isPlural } = derive(key, value);
|
|
1481
1515
|
out.set(path, { key: path, namespace, value, placeholders, isPlural });
|
|
1482
1516
|
} else {
|
|
1483
|
-
addPathEntries(value, path, namespace, derive, out);
|
|
1517
|
+
addPathEntries(value, path, namespace, derive, out, claimedPaths);
|
|
1484
1518
|
}
|
|
1485
1519
|
}
|
|
1486
1520
|
}
|
|
1487
1521
|
function flattenTree(tree, namespace, derive, keyMode = "literal-leaf") {
|
|
1488
1522
|
const out = /* @__PURE__ */ new Map();
|
|
1489
1523
|
if (keyMode === "path-notation") {
|
|
1490
|
-
addPathEntries(tree, "", namespace, derive, out);
|
|
1524
|
+
addPathEntries(tree, "", namespace, derive, out, /* @__PURE__ */ new Set());
|
|
1491
1525
|
return out;
|
|
1492
1526
|
}
|
|
1493
1527
|
addEntries({ namespace, derive, out, claimed: /* @__PURE__ */ new Map() }, [], tree);
|
|
@@ -1609,21 +1643,20 @@ function messagesFromEntries(entries) {
|
|
|
1609
1643
|
}
|
|
1610
1644
|
return out;
|
|
1611
1645
|
}
|
|
1646
|
+
function isEnoent(error) {
|
|
1647
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
1648
|
+
}
|
|
1612
1649
|
async function readDestinationPairs(filePath) {
|
|
1613
|
-
let
|
|
1650
|
+
let content;
|
|
1614
1651
|
try {
|
|
1615
|
-
|
|
1616
|
-
|
|
1652
|
+
content = await readFileContent(filePath);
|
|
1653
|
+
} catch (error) {
|
|
1654
|
+
if (isEnoent(error)) {
|
|
1617
1655
|
return null;
|
|
1618
1656
|
}
|
|
1619
|
-
|
|
1620
|
-
} catch {
|
|
1621
|
-
return null;
|
|
1622
|
-
}
|
|
1623
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
1624
|
-
return null;
|
|
1657
|
+
throw error;
|
|
1625
1658
|
}
|
|
1626
|
-
return Object.entries(
|
|
1659
|
+
return Object.entries(parseArbObject(content));
|
|
1627
1660
|
}
|
|
1628
1661
|
async function buildArbWriteTree(entries, filePath) {
|
|
1629
1662
|
const messages = messagesFromEntries(entries);
|
|
@@ -1724,6 +1757,20 @@ function createNextIntlJsonAdapter() {
|
|
|
1724
1757
|
validateMessage: icuIsValid
|
|
1725
1758
|
});
|
|
1726
1759
|
}
|
|
1760
|
+
function assertNoDottedNestedKey(tree) {
|
|
1761
|
+
for (const [key, value] of Object.entries(tree)) {
|
|
1762
|
+
if (typeof value !== "object") {
|
|
1763
|
+
continue;
|
|
1764
|
+
}
|
|
1765
|
+
if (key.includes(".")) {
|
|
1766
|
+
throw new AdapterError(
|
|
1767
|
+
"MIXED_STRUCTURE",
|
|
1768
|
+
"A nested object key contains a literal dot, which is ambiguous with a dotted path."
|
|
1769
|
+
);
|
|
1770
|
+
}
|
|
1771
|
+
assertNoDottedNestedKey(value);
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1727
1774
|
function assertNotMixed(tree) {
|
|
1728
1775
|
let hasNested = false;
|
|
1729
1776
|
let hasFlatDottedKey = false;
|
|
@@ -1740,6 +1787,7 @@ function assertNotMixed(tree) {
|
|
|
1740
1787
|
"The file mixes flat dotted keys with nested objects."
|
|
1741
1788
|
);
|
|
1742
1789
|
}
|
|
1790
|
+
assertNoDottedNestedKey(tree);
|
|
1743
1791
|
}
|
|
1744
1792
|
async function detectStyle(filePath) {
|
|
1745
1793
|
let parsed;
|
|
@@ -2237,6 +2285,7 @@ function failureSummary(locale, error) {
|
|
|
2237
2285
|
pruned: [],
|
|
2238
2286
|
invalidIcuSource: [],
|
|
2239
2287
|
integrityMismatches: [],
|
|
2288
|
+
providerFailures: [],
|
|
2240
2289
|
generated: [],
|
|
2241
2290
|
notices: [],
|
|
2242
2291
|
error: describeError(error)
|
|
@@ -2248,6 +2297,29 @@ function partition(locales) {
|
|
|
2248
2297
|
return { succeeded, failed };
|
|
2249
2298
|
}
|
|
2250
2299
|
|
|
2300
|
+
// src/flow/batching.ts
|
|
2301
|
+
function chunk(items, size) {
|
|
2302
|
+
const chunks = [];
|
|
2303
|
+
for (let index = 0; index < items.length; index += size) {
|
|
2304
|
+
chunks.push(items.slice(index, index + size));
|
|
2305
|
+
}
|
|
2306
|
+
return chunks;
|
|
2307
|
+
}
|
|
2308
|
+
var GENERIC_PROVIDER_FAILURE_MESSAGE = "The provider call failed.";
|
|
2309
|
+
function classifyProviderFailure(error) {
|
|
2310
|
+
if (error instanceof ProviderError) {
|
|
2311
|
+
return { code: error.code, message: error.message };
|
|
2312
|
+
}
|
|
2313
|
+
return { code: "PROVIDER_CALL_FAILED", message: GENERIC_PROVIDER_FAILURE_MESSAGE };
|
|
2314
|
+
}
|
|
2315
|
+
function subBatchFailedNotice(count, error) {
|
|
2316
|
+
const { code, message } = classifyProviderFailure(error);
|
|
2317
|
+
return {
|
|
2318
|
+
code: "SUB_BATCH_FAILED",
|
|
2319
|
+
message: `A sub-batch of ${count} entries failed (${code}: ${message}) and was withheld; it will be retried next run.`
|
|
2320
|
+
};
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2251
2323
|
// src/flow/notices.ts
|
|
2252
2324
|
function isNotice(value) {
|
|
2253
2325
|
return typeof value === "object" && value !== null && typeof value.code === "string" && typeof value.message === "string";
|
|
@@ -2430,13 +2502,36 @@ async function generatePluralForms(context) {
|
|
|
2430
2502
|
const plan = planPluralGeneration(context.source, context.targetLocale, context.format);
|
|
2431
2503
|
const stale = staleItems(plan.items, context.baseline);
|
|
2432
2504
|
if (stale.length === 0) {
|
|
2433
|
-
return { accepted: [], withheld: [] };
|
|
2505
|
+
return { accepted: [], withheld: [], providerFailures: [], notices: [] };
|
|
2434
2506
|
}
|
|
2435
|
-
const entries = stale.map(syntheticEntry);
|
|
2436
|
-
const result = await context.provider.translateBatch(buildRequest2(context, entries));
|
|
2437
2507
|
const accepted = [];
|
|
2438
2508
|
const withheld = [];
|
|
2439
|
-
|
|
2509
|
+
const providerFailures = [];
|
|
2510
|
+
const notices = [];
|
|
2511
|
+
for (const batch of chunk(stale, context.maxBatchSize)) {
|
|
2512
|
+
const batchNotices = await runGenerationSubBatch(
|
|
2513
|
+
context,
|
|
2514
|
+
batch,
|
|
2515
|
+
accepted,
|
|
2516
|
+
withheld,
|
|
2517
|
+
providerFailures
|
|
2518
|
+
);
|
|
2519
|
+
notices.push(...batchNotices);
|
|
2520
|
+
}
|
|
2521
|
+
return { accepted, withheld, providerFailures, notices };
|
|
2522
|
+
}
|
|
2523
|
+
async function runGenerationSubBatch(context, batch, accepted, withheld, providerFailures) {
|
|
2524
|
+
let result;
|
|
2525
|
+
try {
|
|
2526
|
+
const entries = batch.map(syntheticEntry);
|
|
2527
|
+
result = await context.provider.translateBatch(buildRequest2(context, entries));
|
|
2528
|
+
} catch (error) {
|
|
2529
|
+
for (const item of batch) {
|
|
2530
|
+
providerFailures.push(item.targetKey);
|
|
2531
|
+
}
|
|
2532
|
+
return [subBatchFailedNotice(batch.length, error)];
|
|
2533
|
+
}
|
|
2534
|
+
for (const item of batch) {
|
|
2440
2535
|
const value = result.values.get(item.targetKey);
|
|
2441
2536
|
const integrity = result.integrity.get(item.targetKey);
|
|
2442
2537
|
if (value !== void 0 && integrity?.matches === true) {
|
|
@@ -2449,7 +2544,7 @@ async function generatePluralForms(context) {
|
|
|
2449
2544
|
withheld.push(item.targetKey);
|
|
2450
2545
|
}
|
|
2451
2546
|
}
|
|
2452
|
-
return
|
|
2547
|
+
return readNotices(result);
|
|
2453
2548
|
}
|
|
2454
2549
|
|
|
2455
2550
|
// src/flow/locale-run.ts
|
|
@@ -2499,6 +2594,7 @@ async function runLocale(params) {
|
|
|
2499
2594
|
translated: toTranslate,
|
|
2500
2595
|
generated: [],
|
|
2501
2596
|
integrityMismatches: [],
|
|
2597
|
+
providerFailures: [],
|
|
2502
2598
|
pruned,
|
|
2503
2599
|
notices: sdkNotices
|
|
2504
2600
|
}),
|
|
@@ -2508,12 +2604,14 @@ async function runLocale(params) {
|
|
|
2508
2604
|
const entries = toTranslate.map((key) => params.source.entries.get(key)).filter((entry) => entry !== void 0);
|
|
2509
2605
|
const accepted = /* @__PURE__ */ new Map();
|
|
2510
2606
|
const integrityMismatches = [];
|
|
2607
|
+
const providerFailures = [];
|
|
2511
2608
|
const subBatchNotices = await translateAndCheck(
|
|
2512
2609
|
provider,
|
|
2513
2610
|
params,
|
|
2514
2611
|
entries,
|
|
2515
2612
|
accepted,
|
|
2516
|
-
integrityMismatches
|
|
2613
|
+
integrityMismatches,
|
|
2614
|
+
providerFailures
|
|
2517
2615
|
);
|
|
2518
2616
|
const merged = new Map(target.entries);
|
|
2519
2617
|
for (const key of pruned) {
|
|
@@ -2537,8 +2635,18 @@ async function runLocale(params) {
|
|
|
2537
2635
|
path
|
|
2538
2636
|
);
|
|
2539
2637
|
const pluralNotices = params.generatePlurals ? pluralNoticeFor(params, merged) : sdkNotices;
|
|
2540
|
-
const notices = [
|
|
2541
|
-
|
|
2638
|
+
const notices = [
|
|
2639
|
+
...pluralNotices,
|
|
2640
|
+
...subBatchNotices,
|
|
2641
|
+
...generation.notices
|
|
2642
|
+
];
|
|
2643
|
+
const withheld = /* @__PURE__ */ new Set([
|
|
2644
|
+
...integrityMismatches,
|
|
2645
|
+
...providerFailures,
|
|
2646
|
+
...invalidIcuSource,
|
|
2647
|
+
...generation.withheld,
|
|
2648
|
+
...generation.providerFailures
|
|
2649
|
+
]);
|
|
2542
2650
|
return {
|
|
2543
2651
|
summary: baseSummary({
|
|
2544
2652
|
locale: params.targetLocale,
|
|
@@ -2549,6 +2657,8 @@ async function runLocale(params) {
|
|
|
2549
2657
|
generated: generation.accepted.map((form) => form.targetKey).sort(),
|
|
2550
2658
|
// Withheld generated forms surface alongside withheld translations: both failed integrity.
|
|
2551
2659
|
integrityMismatches: [...integrityMismatches, ...generation.withheld].sort(),
|
|
2660
|
+
// A generation sub-batch whose provider call itself threw is a provider failure, never integrity.
|
|
2661
|
+
providerFailures: [...providerFailures, ...generation.providerFailures].sort(),
|
|
2552
2662
|
pruned,
|
|
2553
2663
|
notices
|
|
2554
2664
|
}),
|
|
@@ -2557,7 +2667,7 @@ async function runLocale(params) {
|
|
|
2557
2667
|
}
|
|
2558
2668
|
async function runGeneration(params, provider) {
|
|
2559
2669
|
if (!params.generatePlurals || provider.kind !== "llm") {
|
|
2560
|
-
return { accepted: [], withheld: [] };
|
|
2670
|
+
return { accepted: [], withheld: [], providerFailures: [], notices: [] };
|
|
2561
2671
|
}
|
|
2562
2672
|
return generatePluralForms({
|
|
2563
2673
|
source: params.source,
|
|
@@ -2568,7 +2678,8 @@ async function runGeneration(params, provider) {
|
|
|
2568
2678
|
provider,
|
|
2569
2679
|
glossary: params.glossary,
|
|
2570
2680
|
tone: params.tone,
|
|
2571
|
-
baseline: params.baseline
|
|
2681
|
+
baseline: params.baseline,
|
|
2682
|
+
maxBatchSize: params.maxBatchSize
|
|
2572
2683
|
});
|
|
2573
2684
|
}
|
|
2574
2685
|
function pluralNoticeFor(params, merged) {
|
|
@@ -2590,27 +2701,35 @@ function baseSummary(parts) {
|
|
|
2590
2701
|
pruned: parts.pruned,
|
|
2591
2702
|
invalidIcuSource: parts.invalidIcuSource,
|
|
2592
2703
|
integrityMismatches: parts.integrityMismatches,
|
|
2704
|
+
providerFailures: parts.providerFailures,
|
|
2593
2705
|
generated: parts.generated,
|
|
2594
2706
|
notices: parts.notices
|
|
2595
2707
|
};
|
|
2596
2708
|
}
|
|
2597
|
-
async function translateAndCheck(provider, params, entries, accepted, integrityMismatches) {
|
|
2709
|
+
async function translateAndCheck(provider, params, entries, accepted, integrityMismatches, providerFailures) {
|
|
2598
2710
|
const notices = [];
|
|
2599
2711
|
for (const batch of chunk(entries, params.maxBatchSize)) {
|
|
2600
|
-
const subNotices = await runSubBatch(
|
|
2712
|
+
const subNotices = await runSubBatch(
|
|
2713
|
+
provider,
|
|
2714
|
+
params,
|
|
2715
|
+
batch,
|
|
2716
|
+
accepted,
|
|
2717
|
+
integrityMismatches,
|
|
2718
|
+
providerFailures
|
|
2719
|
+
);
|
|
2601
2720
|
notices.push(...subNotices);
|
|
2602
2721
|
}
|
|
2603
2722
|
return notices;
|
|
2604
2723
|
}
|
|
2605
|
-
async function runSubBatch(provider, params, batch, accepted, integrityMismatches) {
|
|
2724
|
+
async function runSubBatch(provider, params, batch, accepted, integrityMismatches, providerFailures) {
|
|
2606
2725
|
let result;
|
|
2607
2726
|
try {
|
|
2608
2727
|
result = await provider.translateBatch(buildRequest3(params, batch));
|
|
2609
|
-
} catch {
|
|
2728
|
+
} catch (error) {
|
|
2610
2729
|
for (const entry of batch) {
|
|
2611
|
-
|
|
2730
|
+
providerFailures.push(entry.key);
|
|
2612
2731
|
}
|
|
2613
|
-
return [subBatchFailedNotice(batch.length)];
|
|
2732
|
+
return [subBatchFailedNotice(batch.length, error)];
|
|
2614
2733
|
}
|
|
2615
2734
|
for (const entry of batch) {
|
|
2616
2735
|
const value = result.values.get(entry.key);
|
|
@@ -2623,19 +2742,6 @@ async function runSubBatch(provider, params, batch, accepted, integrityMismatche
|
|
|
2623
2742
|
}
|
|
2624
2743
|
return readNotices(result);
|
|
2625
2744
|
}
|
|
2626
|
-
function subBatchFailedNotice(count) {
|
|
2627
|
-
return {
|
|
2628
|
-
code: "SUB_BATCH_FAILED",
|
|
2629
|
-
message: `A sub-batch of ${count} entries failed and was withheld; it will be retried next run.`
|
|
2630
|
-
};
|
|
2631
|
-
}
|
|
2632
|
-
function chunk(items, size) {
|
|
2633
|
-
const chunks = [];
|
|
2634
|
-
for (let index = 0; index < items.length; index += size) {
|
|
2635
|
-
chunks.push(items.slice(index, index + size));
|
|
2636
|
-
}
|
|
2637
|
-
return chunks;
|
|
2638
|
-
}
|
|
2639
2745
|
function computeLockEntries(params, merged, withheld, generated) {
|
|
2640
2746
|
const lockEntries = {};
|
|
2641
2747
|
const sourceBaseKeys = sourcePluralBaseKeys(params.source);
|
|
@@ -2783,6 +2889,7 @@ var COLUMN_WIDTHS = {
|
|
|
2783
2889
|
[COLUMN.status]: 12,
|
|
2784
2890
|
[COLUMN.translation]: 50
|
|
2785
2891
|
};
|
|
2892
|
+
var TEXT_NUMBER_FORMAT = "@";
|
|
2786
2893
|
function styleHeader(sheet) {
|
|
2787
2894
|
const header = sheet.getRow(HEADER_ROW);
|
|
2788
2895
|
HEADERS.forEach((label, index) => {
|
|
@@ -2798,6 +2905,7 @@ function applyColumnGeometry(sheet) {
|
|
|
2798
2905
|
sheet.getColumn(Number(column)).width = width;
|
|
2799
2906
|
}
|
|
2800
2907
|
sheet.getColumn(COLUMN.sourceHash).hidden = true;
|
|
2908
|
+
sheet.getColumn(COLUMN.translation).numFmt = TEXT_NUMBER_FORMAT;
|
|
2801
2909
|
sheet.views = [{ state: "frozen", ySplit: HEADER_ROW }];
|
|
2802
2910
|
}
|
|
2803
2911
|
function writeRow(sheet, sheetRow) {
|
|
@@ -2806,7 +2914,9 @@ function writeRow(sheet, sheetRow) {
|
|
|
2806
2914
|
row.getCell(COLUMN.source).value = sheetRow.source;
|
|
2807
2915
|
row.getCell(COLUMN.current).value = sheetRow.currentTarget;
|
|
2808
2916
|
row.getCell(COLUMN.status).value = sheetRow.status;
|
|
2809
|
-
row.getCell(COLUMN.translation)
|
|
2917
|
+
const translationCell = row.getCell(COLUMN.translation);
|
|
2918
|
+
translationCell.numFmt = TEXT_NUMBER_FORMAT;
|
|
2919
|
+
translationCell.value = sheetRow.translation === "" ? null : sheetRow.translation;
|
|
2810
2920
|
row.getCell(COLUMN.sourceHash).value = sheetRow.sourceHash;
|
|
2811
2921
|
for (let column = COLUMN.key; column <= COLUMN.sourceHash; column += 1) {
|
|
2812
2922
|
const cell = row.getCell(column);
|
|
@@ -3158,9 +3268,20 @@ function judge(row, sourceEntry, adapter) {
|
|
|
3158
3268
|
}
|
|
3159
3269
|
return void 0;
|
|
3160
3270
|
}
|
|
3271
|
+
function trackBlankDrift(row, params, buckets) {
|
|
3272
|
+
const sourceEntry = params.source.entries.get(row.key);
|
|
3273
|
+
if (sourceEntry === void 0) {
|
|
3274
|
+
return;
|
|
3275
|
+
}
|
|
3276
|
+
const priorHash = params.baseline.get(row.key);
|
|
3277
|
+
if (priorHash !== void 0 && priorHash !== contentHash(sourceEntry)) {
|
|
3278
|
+
buckets.blankDrifted.add(row.key);
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3161
3281
|
function classifyRows(params, buckets) {
|
|
3162
3282
|
for (const row of params.sheet.rows) {
|
|
3163
3283
|
if (row.translation === "") {
|
|
3284
|
+
trackBlankDrift(row, params, buckets);
|
|
3164
3285
|
continue;
|
|
3165
3286
|
}
|
|
3166
3287
|
if (isUnknownKey(row, params.source, params.target)) {
|
|
@@ -3179,9 +3300,20 @@ function classifyRows(params, buckets) {
|
|
|
3179
3300
|
}
|
|
3180
3301
|
}
|
|
3181
3302
|
}
|
|
3303
|
+
function blankRowBaselineNotice(count) {
|
|
3304
|
+
return {
|
|
3305
|
+
code: "BLANK_ROW_BASELINE_RETAINED",
|
|
3306
|
+
message: `${count} row(s) were left blank for a key whose source changed since the row's baseline was recorded; the prior baseline was kept so the drift keeps being reported.`
|
|
3307
|
+
};
|
|
3308
|
+
}
|
|
3182
3309
|
function importLocale(params) {
|
|
3183
3310
|
const diff2 = diffResources(params.source, params.target, { baseline: params.baseline });
|
|
3184
|
-
const buckets = {
|
|
3311
|
+
const buckets = {
|
|
3312
|
+
accepted: /* @__PURE__ */ new Map(),
|
|
3313
|
+
mismatches: [],
|
|
3314
|
+
withheld: /* @__PURE__ */ new Set(),
|
|
3315
|
+
blankDrifted: /* @__PURE__ */ new Set()
|
|
3316
|
+
};
|
|
3185
3317
|
classifyRows(params, buckets);
|
|
3186
3318
|
const rowKeys = new Set(params.sheet.rows.map((row) => row.key));
|
|
3187
3319
|
const invalidIcuSource = [...new Set(params.sourceInvalidIcuKeys)].filter((key) => rowKeys.has(key)).sort();
|
|
@@ -3195,9 +3327,11 @@ function importLocale(params) {
|
|
|
3195
3327
|
pruned: [],
|
|
3196
3328
|
invalidIcuSource,
|
|
3197
3329
|
integrityMismatches: [...buckets.mismatches].sort(),
|
|
3330
|
+
// A workbook import never calls a provider, so a provider-call failure cannot occur here.
|
|
3331
|
+
providerFailures: [],
|
|
3198
3332
|
// Plural generation is a translate-flow concern; the manual workbook import never generates forms.
|
|
3199
3333
|
generated: [],
|
|
3200
|
-
notices: []
|
|
3334
|
+
notices: buckets.blankDrifted.size > 0 ? [blankRowBaselineNotice(buckets.blankDrifted.size)] : []
|
|
3201
3335
|
};
|
|
3202
3336
|
return { summary, accepted: buckets.accepted, withheld: buckets.withheld };
|
|
3203
3337
|
}
|
|
@@ -3231,21 +3365,19 @@ function mergeAccepted(target, accepted) {
|
|
|
3231
3365
|
}
|
|
3232
3366
|
return merged;
|
|
3233
3367
|
}
|
|
3234
|
-
function computeLockEntries2(source, merged, baseline,
|
|
3368
|
+
function computeLockEntries2(source, merged, baseline, accepted) {
|
|
3235
3369
|
const entries = {};
|
|
3236
3370
|
for (const key of merged.keys()) {
|
|
3237
3371
|
const sourceEntry = source.entries.get(key);
|
|
3238
3372
|
if (sourceEntry === void 0) {
|
|
3239
3373
|
continue;
|
|
3240
3374
|
}
|
|
3241
|
-
if (
|
|
3242
|
-
|
|
3243
|
-
if (prior !== void 0) {
|
|
3244
|
-
entries[key] = prior;
|
|
3245
|
-
}
|
|
3375
|
+
if (accepted.has(key)) {
|
|
3376
|
+
entries[key] = contentHash(sourceEntry);
|
|
3246
3377
|
continue;
|
|
3247
3378
|
}
|
|
3248
|
-
|
|
3379
|
+
const prior = baseline.get(key);
|
|
3380
|
+
entries[key] = prior !== void 0 ? prior : contentHash(sourceEntry);
|
|
3249
3381
|
}
|
|
3250
3382
|
return entries;
|
|
3251
3383
|
}
|
|
@@ -3258,7 +3390,7 @@ async function runSheet(ctx, sheet, lock) {
|
|
|
3258
3390
|
}
|
|
3259
3391
|
const target = await readTarget4(ctx.cwd, ctx.config, ctx.adapter, ctx.fs, sheet.locale);
|
|
3260
3392
|
const baseline = baselineFor(lock, sheet.locale);
|
|
3261
|
-
const { summary, accepted
|
|
3393
|
+
const { summary, accepted } = importLocale({
|
|
3262
3394
|
sheet,
|
|
3263
3395
|
source: ctx.source,
|
|
3264
3396
|
target,
|
|
@@ -3282,7 +3414,7 @@ async function runSheet(ctx, sheet, lock) {
|
|
|
3282
3414
|
path
|
|
3283
3415
|
);
|
|
3284
3416
|
}
|
|
3285
|
-
return { summary, lockEntries: computeLockEntries2(ctx.source, merged, baseline,
|
|
3417
|
+
return { summary, lockEntries: computeLockEntries2(ctx.source, merged, baseline, accepted) };
|
|
3286
3418
|
}
|
|
3287
3419
|
async function importWorkbook(input, deps = {}) {
|
|
3288
3420
|
const config = input.config;
|