Add i18n support: per-locale catalogs, URL-driven locale, translated core and examples
CI / full-gate (push) Successful in 2m37s
CI / full-gate (push) Successful in 2m37s
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
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: "a", other: "b" } });
|
||||
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": "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" } });
|
||||
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" } });
|
||||
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" } });
|
||||
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);
|
||||
});
|
||||
Reference in New Issue
Block a user