Architecture review fixes: partials carry the locale, mountable locales/, shared core words
CI / full-gate (push) Successful in 2m36s

This commit is contained in:
2026-08-03 23:12:18 +02:00
parent 245d1ad5b5
commit 6440c543e5
48 changed files with 337 additions and 118 deletions
+15 -5
View File
@@ -11,7 +11,7 @@ test("a complete translation reports nothing", () => {
});
test("a missing or unknown key is reported", () => {
const missing = parity("sv-SE", { "shifts.count": { one: "a", other: "b" } });
const missing = parity("sv-SE", { "shifts.count": { one: "{{count}} pass", other: "{{count}} pass" } });
assert.equal(missing.length, 1);
assert.match(missing[0] ?? "", /missing key "greeting"/);
@@ -21,21 +21,21 @@ test("a missing or unknown key is reported", () => {
});
test("a key must stay the same kind as in the baseline", () => {
const flat = parity("sv-SE", { greeting: "Hej", "shifts.count": "pass" });
const flat = parity("sv-SE", { greeting: "Hej", "shifts.count": "{{count}} pass" });
assert.equal(flat.length, 1);
assert.match(flat[0] ?? "", /"shifts.count" must be a plural message/);
const plural = parity("sv-SE", { greeting: { one: "Hej", other: "Hej" }, "shifts.count": { one: "a", other: "b" } });
const plural = parity("sv-SE", { greeting: { one: "Hej", other: "Hej" }, "shifts.count": { one: "{{count}} pass", other: "{{count}} pass" } });
assert.equal(plural.length, 1);
assert.match(plural[0] ?? "", /"greeting" must be a string/);
});
test("a plural message must cover exactly its own locale's categories", () => {
const short = parity("cs-CZ", { greeting: "Ahoj", "shifts.count": { one: "a", other: "b" } });
const short = parity("cs-CZ", { greeting: "Ahoj", "shifts.count": { one: "{{count}} směna", other: "{{count}} směn" } });
assert.equal(short.length, 1);
assert.match(short[0] ?? "", /"shifts\.count".*cs-CZ.*few, many/);
const long = parity("sv-SE", { greeting: "Hej", "shifts.count": { few: "x", one: "a", other: "b" } });
const long = parity("sv-SE", { greeting: "Hej", "shifts.count": { few: "{{count}} pass", one: "{{count}} pass", other: "{{count}} pass" } });
assert.equal(long.length, 1);
assert.match(long[0] ?? "", /"shifts\.count".*few/);
});
@@ -55,3 +55,13 @@ test("isCatalog accepts strings and plural objects, rejects anything else", () =
assert.equal(isCatalog(null), false);
assert.equal(isCatalog([]), false);
});
test("a translation must interpolate exactly what the baseline does", () => {
const withVars: Catalog = { hi: "Hi {{name}}, you have {{n}} left" };
const check = (catalog: Catalog): string[] => checkCatalog({ baseline: withVars, baselineLocale: "en-US", catalog, locale: "sv-SE" });
assert.deepEqual(check({ hi: "Hej {{name}}, du har {{n}} kvar" }), []);
assert.match(check({ hi: "Hej, du har {{n}} kvar" })[0] ?? "", /"hi" never uses \{\{name\}\}/); // dropped ⇒ a blank on screen
assert.match(check({ hi: "Hej {{namn}}, du har {{n}} kvar" })[0] ?? "", /never uses \{\{name\}\}/); // misspelled ⇒ both problems
assert.match(check({ hi: "Hej {{name}} {{n}} {{extra}}" })[0] ?? "", /uses \{\{extra\}\}/); // never supplied ⇒ renders raw
});