68 lines
3.7 KiB
TypeScript
68 lines
3.7 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { test } from "node:test";
|
|
import { type Catalog, checkCatalog, isCatalog } from "./catalog.ts";
|
|
|
|
const baseline: Catalog = { greeting: "Hello", "shifts.count": { one: "{{count}} shift", other: "{{count}} shifts" } };
|
|
const parity = (locale: string, catalog: Catalog): string[] =>
|
|
checkCatalog({ baseline, baselineLocale: "en-US", catalog, locale });
|
|
|
|
test("a complete translation reports nothing", () => {
|
|
assert.deepEqual(parity("sv-SE", { greeting: "Hej", "shifts.count": { one: "{{count}} pass", other: "{{count}} pass" } }), []);
|
|
});
|
|
|
|
test("a missing or unknown key is reported", () => {
|
|
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"/);
|
|
|
|
const extra = parity("sv-SE", { ...baseline, stray: "x" });
|
|
assert.equal(extra.length, 1);
|
|
assert.match(extra[0] ?? "", /unknown key "stray".*en-US/);
|
|
});
|
|
|
|
test("a key must stay the same kind as in the baseline", () => {
|
|
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: "{{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: "{{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: "{{count}} pass", one: "{{count}} pass", other: "{{count}} pass" } });
|
|
assert.equal(long.length, 1);
|
|
assert.match(long[0] ?? "", /"shifts\.count".*few/);
|
|
});
|
|
|
|
test("the baseline is checked against itself, so an incomplete plural fails at home too", () => {
|
|
assert.deepEqual(checkCatalog({ baseline, baselineLocale: "en-US", catalog: baseline, locale: "en-US" }), []);
|
|
const bad: Catalog = { greeting: "Hello", "shifts.count": { one: "{{count}} shift" } };
|
|
assert.match(checkCatalog({ baseline: bad, baselineLocale: "en-US", catalog: bad, locale: "en-US" })[0] ?? "", /other/);
|
|
});
|
|
|
|
test("isCatalog accepts strings and plural objects, rejects anything else", () => {
|
|
assert.equal(isCatalog({ a: "x", b: { other: "y" } }), true);
|
|
assert.equal(isCatalog({ a: 1 }), false);
|
|
assert.equal(isCatalog({ a: { other: 1 } }), false);
|
|
assert.equal(isCatalog({ a: {} }), false); // an empty plural message says nothing
|
|
assert.equal(isCatalog({ a: { bogus: "x" } }), false); // not a plural category
|
|
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
|
|
});
|