feedback5 W5S: i18n phase 2 coverage — test/i18n.test.js scans the whole client, shared and the server messages (no Chinese literal unwrapped, every msgid translated: extract 0 and check --strict as a test; floors 1 000 catalog entries, 1 350 msgid uses), public/i18n/en.json drops the 64 fragments / renamed msgids the sweep replaced (none is a data text), docs/I18N.md: phase-2 coverage, what stays Chinese and why, the server's game texts, the audit's skip rules
This commit is contained in:
1 parent
0025ceac16
commit
c2e77c7fe9
3 files changed
+67
-90
No files matched your search
+26
-11
@@ -4,7 +4,7 @@
|
||||
|
||||
import { test, afterEach } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { readFileSync, readdirSync, statSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import {
|
||||
@@ -116,7 +116,7 @@ test('server wire format: text keeps the Chinese rendering for older clients; pa
|
||||
|
||||
test('public/i18n/en.json: valid, English values, placeholders kept, the pilot screens and server messages translated', () => {
|
||||
const entries = Object.entries(EN).filter(([k]) => !k.startsWith('_'));
|
||||
assert.ok(entries.length > 800, `seeded from PR #70: ${entries.length}`);
|
||||
assert.ok(entries.length >= 1000, `PR #70's seed + phase 2: ${entries.length}`);
|
||||
for (const [k, v] of entries) {
|
||||
assert.equal(typeof v, 'string', k);
|
||||
assert.ok(v.trim(), `empty translation for ${k}`);
|
||||
@@ -126,25 +126,40 @@ test('public/i18n/en.json: valid, English values, placeholders kept, the pilot s
|
||||
for (const p of want) assert.ok(have.has(p), `${k}: {${p}} missing in "${v}"`);
|
||||
}
|
||||
for (const k of ['开始', '博士代号', '设置', '语言', '同盟模拟', '创建同盟', '加入同盟', '开始模拟', 'toggle::关闭', '整备区已满',
|
||||
'第 {r} 回合 · 最终攻势', '本局禁用{names}盟约,此策略效果可能无法发挥', 'bond-threshold::名', 'list::、', '干员持有', '先锋', '开心',
|
||||
'{names}只能部署在召唤者攻击范围内,已退回整备区', '{name}博士的目标生命值已耗尽', '联防阶段:{names} 迎战突破防线的敌人', '【维多利亚】获得{n}件维式重锤']) {
|
||||
assert.ok(EN[k], `en.json lacks ${k}`);
|
||||
}
|
||||
});
|
||||
|
||||
test('every msgid of the pilot screens, the shell and the server messages is in public/i18n/en.json', async () => {
|
||||
const files = ['public/js/screens/title.js', 'public/js/screens/lobby.js', 'public/js/screens/room.js', 'public/js/ui/settings.js',
|
||||
'public/js/screens/diy.js',
|
||||
'public/js/main.js', 'public/js/ui/toasts.js', 'server/match/match/messaging.js', 'server/match/player/placement.js',
|
||||
'server/match/player/basics.js', 'server/match/player/acquire.js', 'server/match/effectsMeta.js', 'server/match/match/settle.js',
|
||||
'server/match/match/platform.js', 'server/match/match/unitePhase.js', 'server/match/match/bossRounds.js',
|
||||
'server/sim/content/bonds/addon/meta.js', 'server/sim/content/bonds/core.js'];
|
||||
test('the whole client, shared and the server messages: no Chinese literal left unwrapped, every msgid in public/i18n/en.json (extract 0, check --strict)', async () => {
|
||||
// what tools/i18n.mjs scans: public/js (not dev / vendor / assets / fonts), shared, server — server/sim only for the
|
||||
// texts it sends (msg() / ctx.toast), its own t() / N_() are other helpers
|
||||
const SKIP = new Set(['node_modules', 'vendor', 'assets', 'fonts', 'dev']);
|
||||
const files = [];
|
||||
const visit = (rel) => {
|
||||
for (const name of readdirSync(path.join(ROOT, rel)).sort()) {
|
||||
if (SKIP.has(name) || name.startsWith('.')) continue;
|
||||
const r = `${rel}/${name}`;
|
||||
if (statSync(path.join(ROOT, r)).isDirectory()) visit(r);
|
||||
else if (/\.m?js$/.test(name)) files.push(r);
|
||||
}
|
||||
};
|
||||
for (const root of ['public/js', 'shared', 'server']) visit(root);
|
||||
assert.ok(files.length > 300, `${files.length} files`);
|
||||
const missing = [];
|
||||
let used = 0;
|
||||
for (const f of files) {
|
||||
const { msgids, literals } = await scanSource(readFileSync(path.join(ROOT, f), 'utf8'), f);
|
||||
const server = f.startsWith('server/');
|
||||
for (const m of msgids) if ((!server || m.via === 'msg' || m.via === 'server') && !EN[m.msgid]) missing.push(`${f}:${m.line} ${m.msgid}`);
|
||||
if (!server) for (const l of literals) if (!l.reason) missing.push(`${f}:${l.line} not wrapped: ${l.msgid}`);
|
||||
for (const m of msgids) {
|
||||
if (server && m.via !== 'msg' && m.via !== 'server') continue;
|
||||
used++;
|
||||
if (!EN[m.msgid]) missing.push(`${f}:${m.line} ${m.msgid}`);
|
||||
}
|
||||
if (!f.startsWith('server/sim/')) for (const l of literals) if (!l.reason) missing.push(`${f}:${l.line} not wrapped: ${l.msgid}`);
|
||||
}
|
||||
assert.ok(used >= 1350, `msgid uses: ${used} (1 404 at phase 2)`);
|
||||
assert.deepEqual(missing, []);
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user