data-i18n 必须显式标注每个可翻译节点,不可依赖class/id推断或父容器继承;JSON语言包需扁平结构、键名严格对齐;切换语言时必须同步更新document.documentElement.lang并符合BCP 47标准;fetch失败需多级fallback且文件编码统一为UTF-8。

data-i18n 标记必须显式打在每个可翻译节点上
不靠 class/id 推断,也不靠父容器批量继承——JS 不会自动猜键名。漏标一个 <button> 或 <input placeholder>,切换语言后那里就永远是旧文案。
常见漏点包括:
- <select> 里的 <option> 文字(需遍历处理)
- <svg> 内的 <text> 节点(要单独识别 SVGTextElement 类型)
- 动态插入的 DOM(比如 AJAX 加载的弹窗、表格行),插入后必须立即调用翻译函数
- <button> 的 value 属性和 textContent 都可能被用到,得同时覆盖
data-i18n 只改 textContent;想换 placeholder、title、alt,得写 data-i18n-placeholder、data-i18n-title 等带后缀的属性。
JSON 语言包结构必须扁平且键名严格对齐
所有语言文件(locales/zh.json、locales/en.json、locales/ja.json)都必须是顶层键值对,不能嵌套:
{"header_title": "首页", "form_email_required": "邮箱必填"}嵌套结构如 {"ui": {"header": {"title": "首页"}} 会导致 key 查找失败,留白或报错。
立即学习“前端免费学习笔记(深入)”;
关键约束:
- 所有文件键名必须完全一致,哪怕某语言暂未翻译,也要保留键并设为空字符串:"btn_submit": ""
- 键名用英文小写+下划线,避免空格、中文、驼峰(loginBtn 易拼错,登录按钮 无法做 JS 属性访问)
- 文件路径统一用 ./locales/${lang}.json,别硬编码成 ./zh.json
切换语言时 document.documentElement.lang 必须同步更新
只改 document.body.lang 或只改某个 <div lang> 没用——浏览器和屏幕阅读器只认 document.documentElement.lang 这个根声明。
但光设根节点还不够:
- 已存在的带 lang 属性的子元素(比如 <p lang="ja"> 引文、<pre lang="bash"> 代码块)不会自动继承,必须手动遍历更新
- lang 值必须符合 BCP 47 标准:zh-Hans ✅,zh_CN ❌,Chinese ❌,末尾空格也会失效
- 切换后若没同步,后果是:VoiceOver 读错语种、Chrome 不触发自动翻译、中英混排标点间距异常
fetch 加载失败必须有 fallback 链路
fetch('./locales/en.json') 失败时,页面不会报错,只会留空——用户看到一堆未渲染的 data-i18n="xxx",不是 bug 是设计缺陷。
实操 fallback 顺序应为:
- 先试完整语言码(zh-HK)
- 再截主语言(zh)查对应文件
- 最后退到内置默认对象(如 {"header_title": "Welcome"})
- 每层都要 try/catch 包住 fetch() 和 response.json()
额外注意:
- 服务端返回 JSON 时,Content-Type 必须是 application/json,否则 fetch 可能静默失败
- 所有 HTML/CSS/JS/JSON 文件编码必须统一为 UTF-8(无 BOM),否则 meta charset 可能失效,导致乱码或解析中断
工程化索引维护真正难的不是加标记或写 JSON,而是让每个新插入的 DOM 节点、每条新文案、每次语言切换都自动进入这套链路——漏掉任意一环,多语言就变成“半残”状态。



















