feat(web): 平台多语言运行时(i18n,档案 81 · R5)

⚠️ 本提交是**另一执行会话**已完成、且**已上线**的源码侧改动,由本会话代为入库
   (线上 https://alotbuy.com/login.html 已引用 /i18n.js)。

- web/i18n.js(新增 276 行):词条表 + data-i18n / data-i18n-attr 就地替换。
  口径(用户定):**这个项目是全球化的 ⇒ 默认语言 = 英语**;
  优先级 ?lang= → cookie `dsh_lang` → navigator.language(**仅命中支持列表才用**)→ `en`;
  ⚠️ cookie 名不得含平台内部名(verify-static 的 BANNED 去痕迹约束会拦)。
- web/{index,login,register,wake}.html:接入 <script src="/i18n.js"> + 文案打标
  + 页脚语言下拉(中性样式、不新增页面)。
- scripts/verify-static.mjs:新增「**已迁移多语言的页面不得再出现裸中文文案**」
  —— I18N_PAGES 白名单(迁一页加一个,未迁页仍允许中文,不会误拦);
  判定前先剥掉 HTML/JS 注释(注释不算文案)。
- scripts/verify-platform-admin-section.mjs:随「偏好设置 = 语言切换(全角色)」新增分区数断言
  (admin 4 个分区 / 非 admin 3 个;切换走官方 ctx.locale.setLocale,语言 id 与官方 LOCALE_IDS 一致)。

验证:node scripts/verify-static.mjs ✓ | node scripts/verify-platform-admin-section.mjs ✓(本会话复核已跑通)
This commit is contained in:
admin committed 2026-09-15 18:54:02 +08:00
1 parent 39a1f2e31b
commit b617cdbaa7
7 files changed
+407 -56

No files matched your search

+25 -3
View File
@@ -184,7 +184,8 @@ function clsAll(tree) {
// ── 注册与门禁 ────────────────────────────────────────────────────────────────
// 档案 87 起 = 3 个:模型设置(全角色)+ 功能管理(全角色)+ 系统管理(仅 admin)
ok("admin 视角下注册了三个 settings.section", regs.length === 3, "-> " + regs.map(r => r.meta.id).join(", "));
// 档案 81 · R5(2026-09-15)新增「偏好设置」= 语言切换(全角色,order 99)⇒ 共 **4** 个
ok("admin 视角下注册了四个 settings.section", regs.length === 4, "-> " + regs.map(r => r.meta.id).join(", "));
const pa = regs.find(r => r.meta.id === "platform-admin");
ok("存在 platform-admin 分区", !!pa);
if (pa) ok("其 order = 102", pa.meta.order === 102);
@@ -212,14 +213,35 @@ ok("非 admin:不出现任何功能项", !PORTAL_ITEMS.some(k => tUser.include
mod.apply(ctx);
await new Promise((r) => setTimeout(r, 80));
const ids = regs.map(r => r.meta.id).sort();
ok("非 admin:注册 2 个分区(功能管理 + 模型设置;不含系统管理)",
ids.length === 2 && ids[0] === "business-plugins" && ids[1] === "model-settings",
ok("非 admin:注册 3 个分区(偏好设置 + 功能管理 + 模型设置;不含系统管理)",
ids.length === 3 && ids[0] === "business-plugins" && ids[1] === "model-settings" && ids[2] === "preferences",
"-> " + ids.join(", "));
regs = []; ROLE = "admin";
mod.apply(ctx);
await new Promise((r) => setTimeout(r, 80));
}
// ── 档案 81 · R5(2026-09-15):「偏好设置」= 语言切换(全角色,走官方 locale)──────
// 口径:默认英语(官方 FALLBACK_LOCALE="en");切换走 ctx.locale.setLocale(id),
// 语言 id 必须与官方 LOCALE_IDS 一致(en / zh)。本断言同时充当「该栏真能渲染」的冒烟。
{
regs = []; ROLE = "active";
mod.apply(ctx);
await new Promise((r) => setTimeout(r, 80));
const pref = regs.find(r => r.meta.id === "preferences");
ok("存在 preferences 分区(语言切换)", !!pref);
if (pref) {
ok("其 order = 99(排在模型设置之前)", pref.meta.order === 99, "-> " + pref.meta.order);
const tPref = text(await render(pref.Comp)).join(" | ");
ok("偏好设置:含「界面语言」标签 + 英文/中文两个语言项",
tPref.includes("界面语言") && tPref.includes("English") && tPref.includes("中文"),
"-> " + tPref.slice(0, 140));
}
regs = []; ROLE = "admin";
mod.apply(ctx);
await new Promise((r) => setTimeout(r, 80));
}
// ── 档案 87「模型设置」分区:三条口径必须体现在界面上 ─────────────────────────
{
const ms = regs.find(r => r.meta.id === "model-settings");