feedback5: merge W5AD — language packs as the first type of a generic content pack: a language is a file in the language folder (public/i18n/<code>.json with a _meta manifest, optional data/i18n/<code>.json) or a pack folder (packs/<id>/pack.json); the server lists them live at /packs/index.json, the menu shows every pack, strings fall back pack → base → declared fallback → Chinese, broken placeholders are skipped, plurals follow Intl.PluralRules (zh / en unchanged); tools i18n.mjs template / check, packs.mjs list / index, build-i18n --lang ja|ko|zh-TW; the release zips ship packs/ and packs/index.json; docs/PACKS.md, docs/I18N.md §5; §25.16 for now (renumbered next); golden: unchanged vs feedback5; vs W5AD exactly what feedback5 changed since f1df0d3 (CB5's 18 bot matches, CB2's roster 13 / bonds 7 / fields 6, CB4's roster-016 / 7 boss fields / standin-04/06/08 / diy-028/124)

No conflicts outside docs/history/0.2.0.md; the §25 intro keeps one sentence per stream (W5AD's line that also named
the hotkeys' §25.15 joined to CB4's).

Gate: non-browser suites 4981 / 4981 (test/i18n-packs 12 new, test/package 1 new), npm run golden ok,
test/golden.test.js 9 / 9, lint 0 errors, check:imports ok, typecheck ok, docs-consistency + docs-paths 31 / 31.
This commit is contained in:
sganggs committed 2026-10-07 03:27:10 +08:00
commit 66cdf493fc
30 files changed
+2670 -288

No files matched your search

+63 -2
View File
@@ -34,7 +34,8 @@ redeploy, 赫默's drone at full SP, the countdown summons' 无敌 / 禁疗 / ti
DoT damage, 伺夜's 狼影 and the 吟游者 trait answered) → §25.16; the community reports of 2026-10-06, stream CB4 (enemies, bosses, waves, movement: the 突袭 landing on the 临时整备区 row and the other half of the
联防 map, a silenced 萨卡兹枯朽战士's death pollution, 萨卡兹穿刺手's speed after a stun and its first hit, 自制投石机 blocked on the 排气格栅; the briefing types'
7–9 waves, 新硎's 断刃, a freed prisoner in 联防 and 鸭爵's two swaps in one wave already answered; the moving leaders' routes
checked; 野鬃, 锏's pulls, 昆图斯's footprint and the leaders' attack rate not reproduced) → §25.18.
checked; 野鬃, 锏's pulls, 昆图斯's footprint and the leaders' attack rate not reproduced) → §25.18; the rebindable shortcuts
→ §25.15; language packs, the first type of content packs (a language is a file in the language folder) → §25.16.
Decisions of the owner: 2026-10-04 — the server stays on plain `node:http` + `ws`, no web framework (§25.1);
2026-10-05 — the 补位 plan (§25.3); the 自选编队 rules, the collab operators out of the pool, ISW-A refused (§25.4);
@@ -47,7 +48,8 @@ a melee 突袭 member lands where it can block (§25.11); 华法琳's granted tr
lock (§25.13.2); a newly merged elite 拉普兰德 counts as a new 拉普兰德 (§25.13.3); the leader pool = bloodPoint × the players
alive at the fight's start, adopting PR #209 and replacing 「保持固定血量」 (§25.13.4); flying units at 1.3 tiles and the two
model quirks of PR #211 (§25.13.5); the bounty pays whenever its enemy dies (relayed with the community
reports, §25.14.3).
reports, §25.14.3); 2026-10-07 — a language is a file in the language folder, and the language pack is the first type
of a general content-pack mechanism (§25.16).
### 25.1 Refactor and tooling: the big files split, golden results, lint, types, import boundaries (refactor plan; owner's decision of 2026-10-04: no web framework) — `server/http/`, `server/match/match/`, `server/match/player/`, `server/sim/battle/`, `content/enemies/`, `kits/ops/`, `public/js/ui/gameLogic/`, `render/fx/`, `render/app/`, `screens/game/`, `tools/golden.mjs`, `tools/check-imports.mjs`, `eslint.config.js`, `jsconfig.json`
@@ -1505,3 +1507,62 @@ briefing types' share of the waves).
卢西恩 attacks only its blocker and blinks past it. A leader and a situation from the reporter would show what differs.
- Golden: unchanged. Pinned: `test/sim/feedback5-cb4-pins.test.js` (the four interval leaders at ≥ max − 1 attacks).
### 25.16 Language packs: a language is a file in the language folder, the first type of content packs (W5AD; the owner's decisions of 2026-10-07) — `shared/packs.js`, `shared/i18nPacks.js`, `shared/i18n.js`, `shared/i18nData.js`, `server/packs.js`, `server/http/static.js`, `public/js/ui/lang.js`, `public/js/data.js`, `tools/i18n.mjs`, `tools/packs.mjs`, `tools/build-i18n.mjs`, `tools/package.mjs`, docs/I18N.md §5, docs/PACKS.md
- **Seen**: after §25.2 English was wired in by name — the language list `['zh', 'en']`, the switch's labels, English
checks in the title screen, `fmtNum`, the tab title, a CSS rule, the overlay URL pattern — so another language meant
editing code, and players asked to translate into their own languages.
- **Decision**: the owner's decision of 2026-10-07 — putting an adapted language file into the language folder is all
it takes for the game to offer that language, so the community can add languages themselves; the same day, design the
language pack as the first type of a general content-pack mechanism (later: resource packs for art / audio, then
server-side data packs) with one manifest, one registry and one index.
- **Now** (docs/I18N.md §5 for translators, docs/PACKS.md for the mechanism):
- **A pack** is a manifest plus its files, in one of two layouts the same registry reads: the single file
`public/i18n/<code>.json` (manifest: its `_meta` block; optional game texts `data/i18n/<code>.json`) — the
translator's way and how English ships — or a folder `packs/<id>/pack.json` naming its files (every future type).
The manifest: type (`lang`; `assets` and `data` planned: parsed, listed as not supported, never served), version,
the app versions it targets (a range; outside it a language pack still loads and is flagged), its name in its own
language and in English, authors / credits / licence, and the type's fields (`lang`, `base`, `fallback`, `complete`,
`numberUnits`). English carries one like any pack (`complete: true`).
- **Discovery**: `server/packs.js` scans the language folders and `packs/` at start and again when their files
change (looked at on each index or pack-file request, at most once a second), validates the manifests, and answers
`GET /packs/index.json` (live, never cached; packs of supported types with the URLs of their files) and
`GET /packs/<id>/<file>` (only a file a manifest names, with its role's extension, inside the pack folder). A static
host uses `node tools/packs.mjs index` (packs/index.json); the release zips ship packs/ and that file. A broken
manifest, a planned type, a second pack for an id or a language: skipped with the reason in the server log and
`node tools/packs.mjs list`.
- **The client** lists every `lang` entry under its own name (title screen and 设置: buttons up to four languages, a
list beyond); `?lang=<code>` and the stored choice resolve to a listed pack (`en-US` → `en`; a pack that is gone →
Chinese); without an index the stored pack still loads by its code. Nothing in the client names English: the title
screen lays out by the script of the pack's title (`scriptOf`: an alphabetic title takes English's layout), the tab
title and `<html lang>` follow the pack, `fmtNum` takes `numberUnits` (Chinese 万 / 亿; without them K / M / B).
- **Fallback chain** per string: the pack → its base (`pt-BR` → `pt`; `zh-TW` none) → the packs `_meta.fallback`
names (English) → the Chinese msgid; game texts per text (each from the first overlay of the chain that has it:
`applyFileOverlay(json, overlay, onto)`, `data.setLocale(lang, chain)`).
- **Placeholders**: a translation that drops a plain placeholder of its msgid, uses one the caller does not pass, or
has a broken plural form is skipped like a missing one (the next language of the chain shows; a development page
warns once on the console; `tools/i18n.mjs check` checks the call sites). **Plurals**: `{n|one|other}` picks by
`Intl.PluralRules` (English exactly as before), and named categories serve other languages
(`{n|one:…|few:…|many:…|other:…}`).
- **Tools**: `node tools/i18n.mjs template <code>` (a skeleton: `_meta` and every msgid; on an existing pack only
the msgids it lacks), `check <code> | --all` (coverage; errors; a complete pack must have every msgid);
`node tools/build-i18n.mjs --lang ja | ko | zh-TW` builds a pack's game texts from the official JP / KR / TW client
(ArknightsAssets/ArknightsGamedata `jp/`, `kr/`, `tw/`, each with both 盟约 seasons, checked 2026-10-07: ja 96.5 %,
ko 97.4 %, zh-TW 91.1 % of 22 114 texts); a language without a client uses `--dict` (a community dictionary) or no
game texts at all (they follow the chain). No language but English ships: there is no Japanese, Korean or
Traditional Chinese UI pack yet, and a game-text file without its UI pack is not listed.
- **The output in Chinese and English did not change**: `test/i18n-packs.test.js` renders every msgid of
`public/i18n/en.json` in both languages with sample params and compares it with the renderer of 0.2.0 before packs;
`node tools/build-i18n.mjs --check` still finds `data/i18n/en.json` up to date. One English entry was added, the tab
title (`卫戍协议:盟约 · STRONGHOLD PROTOCOL`, the same text as before).
- [ASSUMED]: the menu turns into a list above four languages; two plain plural forms follow the language's rules (only
-1 renders differently from before in English, "-1 second"); a regional code falls back to its language's pack before
the declared fallbacks; with two packs for one language the language folder's file wins; a pack outside its app range
still loads; a development page is a dev build or one served from localhost.
- Golden: unchanged. Tests: `test/i18n-packs.test.js` (12: discovery of both layouts and of broken / planned /
duplicate packs, a pack dropped in or removed while the server runs, the file allowlist, manifests and app ranges,
the chain, placeholders, plurals in English and Russian, the per-text game-text fallback, the client loader and menu,
the template / check / index tools, build-i18n's `--lang` and `--dict`, zh / en unchanged), `test/package.test.js` (both layouts ship, a local pack does
not, the generated index), `test/ui/i18n.e2e.test.js` (`SP_E2E=1`: a pack file and a pack folder dropped in while the
server runs show in the menu and switch, English filling the gaps; a removed pack sends the stored choice back to
Chinese); `test/i18n.test.js`, `test/i18n-data.test.js`, `test/i18n-server.test.js` unchanged and green.