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:
1 parent
39a1f2e31b
commit
b617cdbaa7
7 files changed
+407
-56
No files matched your search
@@ -16,6 +16,8 @@ const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const WEB = join(ROOT, 'web')
|
||||
const BANNED = 'dshs' // 用户可见面不得出现的平台内部名
|
||||
const WAKE_IDS = ['spin', 'step', 'acts', 'retry', 'note'] // wake.html 内联 JS 依赖的 id
|
||||
// 档案 81 · R5:已完成多语言迁移的页面(**迁移一页加一个**)。这些页不得再出现裸中文文案。
|
||||
const I18N_PAGES = ['login.html', 'register.html', 'wake.html', 'index.html']
|
||||
|
||||
let bad = 0
|
||||
const pages = readdirSync(WEB).filter((f) => f.endsWith('.html'))
|
||||
@@ -24,11 +26,27 @@ console.log('=== 静态页不变量(' + pages.length + ' 页)===')
|
||||
for (const f of pages) {
|
||||
const s = readFileSync(join(WEB, f), 'utf8')
|
||||
const problems = []
|
||||
const title = /<title>([^<]*)<\/title>/.exec(s)
|
||||
const title = /<title[^>]*>([^<]*)<\/title>/.exec(s)
|
||||
if (!title || title[1].trim() === '') problems.push('缺 <title> 或为空')
|
||||
else if (title[1].includes(BANNED)) problems.push('title 含内部平台名: ' + title[1])
|
||||
if (s.includes(BANNED)) problems.push('页面正文含内部平台名(去痕迹约束)')
|
||||
|
||||
// 档案 81 · R5:**已迁移多语言的页面不得再出现裸中文文案** —— 用户可见文案必须走词条
|
||||
// (`data-i18n` / `I18N.t`)。注释不算文案,故先剥掉 HTML 与 JS 注释再判。
|
||||
// 只对白名单页生效 ⇒ 未迁移页仍允许中文,不会误拦(迁移一页就往 I18N_PAGES 加一个)。
|
||||
if (I18N_PAGES.includes(f)) {
|
||||
const stripped = s
|
||||
.replace(/<!--[\s\S]*?-->/g, '')
|
||||
.replace(/\/\*[\s\S]*?\*\//g, '')
|
||||
.replace(/^[ \t]*\/\/.*$/gm, '')
|
||||
.replace(/[ \t]\/\/[^\n]*/g, '') // 行尾注释(`code // 说明`)也要剥(`https://` 不会被误伤:左邻是 `:` 不是空格)
|
||||
const cjk = stripped.split('\n').map((l, i) => [i + 1, l]).filter(([, l]) => /[\u4e00-\u9fa5]/.test(l))
|
||||
if (cjk.length) {
|
||||
problems.push('已迁移多语言但仍有裸中文文案(行 ' + cjk.slice(0, 3).map(([n]) => n).join('/') + '…)')
|
||||
}
|
||||
if (!s.includes('/i18n.js')) problems.push('已迁移多语言但未引入 /i18n.js')
|
||||
}
|
||||
|
||||
if (f === 'wake.html') {
|
||||
for (const id of WAKE_IDS) {
|
||||
if (!new RegExp('id="' + id + '"').test(s)) problems.push('缺 id="' + id + '"(内联 JS 依赖)')
|
||||
|
||||
Reference in new issue
Block a user