最可控方案是data-i18n标记+JSON语言包;需显式为所有文本节点及placeholder/title/alt等属性加对应后缀,同步更新document.documentElement.lang、动态节点与localStorage,并确保语言包扁平、BCP 47命名、加载有fallback。

直接用 data-i18n 属性标记 + 外部 JSON 语言包是最可控、最易调试的结构,不依赖框架、不破坏语义、SEO 友好,且能避开 XSS 和状态丢失风险。
怎么给 HTML 元素打翻译标记才不会漏掉内容
所有需要切换语言的文本节点,都必须显式加 data-i18n 属性,值为统一键名(如 "header_title");不能靠 id 或 class 推断,JS 不会自动识别。
-
placeholder、title、alt、value这类属性需单独处理:用data-i18n-placeholder、data-i18n-title等带后缀的属性,否则切换后这些字段仍为旧语言 -
select的option文字不能只标在select上,得遍历每个option,查语言包中"xxx.options"数组逐个赋值 -
label必须标记,否则屏幕阅读器读不到新文案;SVG节点也要加data-i18n并在 JS 中识别SVGTextElement类型做特殊处理 - 别在
<script></script>、、里加data-i18n——它们不渲染为可见文本,加了无效
JSON 语言包路径和结构怎么写才不翻车
路径必须动态拼接,结构必须扁平对齐,否则加新语言或改键名就会触发大面积空白。
- 路径用
./i18n/${lang}.json,别硬编码成./zh.json;否则加日语就得改 JS 逻辑 - 文件结构必须全扁平:
{"header_title": "首页", "btn_submit": "提交"}✅;嵌套对象或缺失字段会导致langPack[key]为undefined,赋给textContent就是空字符串 - 所有语言文件键名严格一致;哪怕某语言暂时没翻译,也得留空字符串或占位符,不能删键
- BCP 47 格式要准:
zh-Hans可以,zh_Hans或chinese会被浏览器和:lang()选择器忽略
切换语言时 DOM 更新必须同步哪些东西
只换文案是半残操作。屏幕阅读器、字体回退、断字、拼写检查全靠 lang 属性驱动,不更新等于白切。
- 必须设
document.documentElement.lang = 'zh-Hans',但仅这样远远不够 - 必须遍历所有已有
lang属性的元素,按需更新:术语引用(如)加data-keep-lang跳过;用户生成内容则同步更新 - 表单控件的
value建议跳过更新(避免干扰提交值),优先覆盖textContent;按钮文字用textContent统一处理更稳 - 动态插入的内容(Toast、confirm、AJAX 返回的提示)无法靠
data-i18n自动捕获,得封装t('common_error')函数手动调用
加载失败或 key 缺失时页面为什么一片空白
因为 fetch('./i18n/en-US.json') 报错或 langPack['missing_key'] 是 undefined,直接赋给 textContent 就是空字符串——用户看到的不是“未翻译”,而是大片空白或原始键名。
- 必须用
try/catch包住整个fetch()+response.json()流程,捕获TypeError: Failed to fetch和 404 -
response.ok要判断,HTTP 非 2xx 状态码不能当成功处理 - 降级不能只 fallback 到内置默认对象,还得 fallback 到备用语言文件(如用户选
zh-HK但只有zh.json,就加载zh.json) - 首次加载别只读
localStorage——用户可能清过缓存,应优先用navigator.language,再 fallback 到内置默认
最容易被忽略的是:lang 属性变更不触发子节点继承更新,每个带 lang 的元素都得手动重设;还有 option、SVG 、动态弹窗这三类内容,90% 的实现漏掉其中至少一类。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











