必须为所有可翻译属性显式添加对应data-i18n后缀(如-placeholder、-title、-alt),仅设data-i18n仅更新textcontent;lang属性须逐节点设置,动态dom需手动触发翻译,语言包加载须fallback降级。

直接把旧 HTML 里的中文硬编码替换成 data-i18n 键名,不补属性、不改 lang、不处理动态节点,上线后必然漏翻、闪屏、读错语音——这不是迁移,是埋雷。
怎么标记元素才不会漏掉 placeholder/title/alt
只给 <button>提交</button> 加 data-i18n="btn_submit",但没加 data-i18n-title,鼠标悬停提示还是英文;<img alt="用户头像"> 没加 data-i18n-alt="avatar_desc",屏幕阅读器照读旧文本。
-
textContent类文案(<h1></h1>、<p></p>、<label></label>)必须有基础data-i18n键 - 含
placeholder的<input>或<textarea></textarea>,额外加data-i18n-placeholder - 含
title、alt、aria-label的元素,必须对应加data-i18n-title、data-i18n-alt、data-i18n-aria-label -
value属性跳过翻译(属于用户输入数据),但<button type="submit"></button>的显示文字建议统一走textContent更新 - 别在
<script></script>、<style></style>、<pre class="brush:php;toolbar:false;"></pre>内部写data-i18n——这些节点不参与文本渲染,JS 替换无效
JSON 语言包加载失败时页面为啥一片空白
fetch 失败、404、返回 text/plain MIME、或 key 缺失时,JS 查不到值就留空,用户看到的是空按钮和空标题,而不是 fallback 文案。
- 路径必须用模板字符串:
./locales/${lang}.json,别硬写成./zh.json - fetch 外层套
try/catch,内部检查response.ok和response.headers.get('content-type')?.includes('application/json') - fallback 顺序:先试完整 BCP 47 码(如
zh-HK),再截主语言(zh),最后退到默认语言(如en) - 所有语言包结构扁平且键名完全对齐;某语言暂未翻译,也要保留键并设为空字符串:
"nav_settings": ""
切换语言后为什么标点乱、字体糊、语音错
只改 document.documentElement.lang = 'zh-Hans',但 <p lang="en">API</p> 和 <pre class="brush:php;toolbar:false;" lang="bash"></pre> 仍保持原 lang 值——浏览器和屏幕阅读器按每个元素自身 lang 属性决定标点宽度、字体回退链、语音语调,不继承根节点。
- 所有含文本的语义化标签(
<h1></h1>、<p></p>、<section></section>、<footer></footer>)都必须显式设lang,值与当前语言包一致(如lang="zh-Hans") - 已有明确多语言用途的元素(如
<pre class="brush:php;toolbar:false;" lang="bash"></pre>、<code lang="sql">)切换时保留原lang,不覆盖 - 更新 DOM 前用
getComputedStyle记录滚动位置,更新完立刻window.scrollTo()恢复,否则页面跳顶 -
<title></title>和<meta name="description">不继承的lang,必须单独更新
动态插入的 DOM 为什么始终不翻译
AJAX 返回的弹窗、分页表格新行、懒加载模块插入后,里面的 data-i18n 还是原始键名,比如 <h2 data-i18n="modal_title">modal_title</h2>,因为 i18n 函数只在初始化时跑一次,不监听 DOM 变更。
- 每次插入新节点后,立即调用翻译函数(如
translateNode(modalEl)或translateChildren(tableBody)) - 不要依赖 MutationObserver 自动扫描——性能差、易漏、无法控制 scope;明确在插入后手动触发
- 含 HTML 结构的文案(如
"请阅读@#@#@#@#@#@#@#@#@#@0")必须用innerHTML赋值,且语言包里对应值要是可信纯 HTML 片段(无用户输入、不执行 JS) - 表单控件(如
<select></select>选项、<option></option>文本)插入后也需遍历更新,不能只刷父容器
最易被忽略的点:语言包键名大小写敏感、lang 属性值必须严格符合 BCP 47(zh-Hans ≠ zh-CN)、localStorage 存的语言偏好不能覆盖服务端通过 Accept-Language 注入的初始值——这些细节不出错,整个迁移才算真正落地。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











