game5 / client / test / i18n.test.mjs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// Catalog fidelity — the CLJS replacement for the TS-era MsgKey type check.
// Two gates: every EN key exists in ro/hu with nothing stray (a missing
// translation fails the suite), and every message VALUE is byte-equal to the
// TS catalogs' snapshot in vectors/i18n.json (so the port never quietly
// reworded a string).
import test from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import * as lib from "../test-dist/testlib.js";

const ts = JSON.parse(readFileSync(new URL("./vectors/i18n.json", import.meta.url), "utf8"));
const en = [...lib.catalogKeys("en")];

test("EN catalog is non-empty and carries the social.* block", () => {
  assert.ok(en.length >= 60);
  assert.ok(en.some((k) => k.startsWith("social.")));
});

for (const lang of ["ro", "hu"]) {
  test(`${lang} covers every EN key, and nothing else`, () => {
    assert.deepEqual([...lib.catalogKeys(lang)], en);
  });
}

for (const lang of ["en", "ro", "hu"]) {
  test(`${lang} messages are byte-identical to the TS catalog`, () => {
    assert.deepEqual([...lib.catalogKeys(lang)], Object.keys(ts[lang]).sort(), `${lang} key set`);
    for (const k of Object.keys(ts[lang])) {
      const got = lib.catalogMsg(lang, k);
      const want = ts[lang][k];
      if (typeof want === "string") {
        assert.equal(got, want, `${lang} ${k}`);
      } else {
        // a plural site: compare the variant map, categories included
        assert.deepEqual(JSON.parse(JSON.stringify(got)), want, `${lang} ${k}`);
      }
    }
  });
}

test("plural sites: ro one/few/other, hu other-only, en one/other", () => {
  const cats = (lang, k) => Object.keys(JSON.parse(JSON.stringify(lib.catalogMsg(lang, k)))).sort();
  for (const k of ["status.linked"]) {
    assert.deepEqual(cats("en", k), ["one", "other"], `en ${k}`);
    assert.deepEqual(cats("ro", k), ["few", "one", "other"], `ro ${k}`);
    assert.deepEqual(cats("hu", k), ["other"], `hu ${k}`);
  }
});

test("wire-adjacent strings are never translated (brand + protocol)", () => {
  for (const lang of ["en", "ro", "hu"]) {
    for (const k of lib.catalogKeys(lang)) {
      const m = lib.catalogMsg(lang, k);
      if (typeof m === "string") assert.ok(!m.includes("/lampion/"), `${lang} ${k}`);
    }
  }
});

test("every {placeholder} in EN survives into ro and hu", () => {
  const holes = (m) => {
    const s = typeof m === "string" ? m : Object.values(JSON.parse(JSON.stringify(m))).join(" ");
    return [...s.matchAll(/\{(\w+)\}/g)].map((x) => x[1]).sort();
  };
  for (const k of en) {
    const want = [...new Set(holes(lib.catalogMsg("en", k)))];
    for (const lang of ["ro", "hu"]) {
      const got = [...new Set(holes(lib.catalogMsg(lang, k)))];
      assert.deepEqual(got, want, `${lang} ${k}`);
    }
  }
});

static mirror of HEAD · about · clone: git clone https://git.ardegazu.ro/game5.git