game2 / 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
72
73
74
75
76
77
78
79
80
81
82
83
// Catalog completeness AND a byte-exact snapshot.
//
// The completeness half is the CLJS replacement for the TS-era MsgKey type
// check: every EN key must exist in ro/hu, no stray keys, and the plural shapes
// carry the documented variants.
//
// The snapshot half is what proves the port CHANGED NO STRINGS: the catalogs
// were transliterated from the TypeScript line by line (never retyped), and
// every one of the 147 keys x 3 languages is compared against the values the
// real TS modules exported.
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 v = 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 >= 40);
  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);
  });
}

test("the key set matches the TypeScript catalogs exactly", () => {
  assert.deepEqual(en, v.keys);
  for (const lang of ["en", "ro", "hu"]) {
    assert.deepEqual([...lib.catalogKeys(lang)], Object.keys(v.catalogs[lang]).sort(), lang);
  }
});

test("every string is byte-identical to the TypeScript original", () => {
  for (const lang of ["en", "ro", "hu"]) {
    const got = lib.catalogEntries(lang);
    for (const [k, want] of Object.entries(v.catalogs[lang])) {
      if (typeof want === "string") {
        assert.equal(got[k], want, `${lang} ${k}`);
      } else {
        assert.deepEqual({ ...got[k] }, want, `${lang} ${k} (plural variants)`);
      }
    }
    assert.deepEqual(Object.keys(got).sort(), Object.keys(v.catalogs[lang]).sort(), lang);
  }
});

test("plural shapes: ro one/few/other, hu other-only, en one/other", () => {
  for (const lang of ["en", "ro", "hu"]) {
    for (const k of lib.catalogKeys(lang)) {
      const got = lib.catalogMsg(lang, k);
      const want = v.shapes[lang][k];
      if (want === "s") assert.equal(typeof got, "string", `${lang} ${k} should be a plain string`);
      else assert.deepEqual([...got], want, `${lang} ${k} variants`);
    }
  }
  assert.deepEqual([...lib.catalogMsg("en", "hud.lines")], ["one", "other"]);
  assert.deepEqual([...lib.catalogMsg("ro", "hud.lines")], ["few", "one", "other"]);
  assert.deepEqual([...lib.catalogMsg("hu", "hud.lines")], ["other"]);
});

test("romanian keeps comma-below diacritics (ș ț, never ş ţ)", () => {
  for (const k of lib.catalogKeys("ro")) {
    const m = lib.catalogMsg("ro", k);
    const strings = typeof m === "string" ? [m] : Object.values({ ...lib.catalogEntries("ro")[k] });
    for (const s of strings) {
      assert.ok(!s.includes("ş"), `ro ${k}: cedilla ş`);
      assert.ok(!s.includes("ţ"), `ro ${k}: cedilla ţ`);
    }
  }
});

test("no catalog value leaks the wire protocol prefix", () => {
  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("/game2/2/"), `${lang} ${k}`);
    }
  }
});

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