必须显式为所有含文本的语义化标签设置lang属性,严格遵循bcp 47格式(如zh-hans),并为data-i18n标记覆盖placeholder、alt等可翻译属性,动态dom需手动触发翻译,语言包加载须校验mime类型并设fallback。

只改 document.documentElement.lang 是无效的
浏览器和屏幕阅读器不继承根节点的 lang,而是逐个检查每个元素自身的 lang 属性来决定标点间距、字体回退、语音朗读方式。只设 document.documentElement.lang = 'zh-Hans',<p></p> 里的顿号仍按英文渲染,<img alt="logo"> 的替代文本还是被读成英文,<pre class="brush:php;toolbar:false;" lang="bash"></pre> 甚至可能被中文字体覆盖。
必须显式给所有含文本的语义化标签加 lang:包括 <h1></h1>、<p></p>、<section></section>、<footer></footer> 等。已有 lang 的特殊元素(如 <pre class="brush:php;toolbar:false;" lang="bash"></pre>)要保留原值,不能批量覆盖——这是合法的多语言混排场景。
-
<script></script>和<style></style>内部写lang没意义,它们不参与文本渲染 -
<title></title>和<meta name="description">完全不继承的lang,必须单独设 - 值必须严格符合 BCP 47 格式,
zh_CN或chinese会被忽略,正确写法是zh-Hans、en-US
data-i18n 标记必须覆盖所有可翻译属性
data-i18n 默认只替换 textContent,对 placeholder、alt、title、aria-label 等属性完全无效。常见错误是只给按钮加 data-i18n="btn_submit",结果输入框提示仍是英文,<label for="email"></label> 文字翻了但 for 没同步,点击失效。
每个需翻译的元素至少要有基础 data-i18n 键;若含 placeholder,必须额外加 data-i18n-placeholder;同理 data-i18n-alt、data-i18n-title。不要给 value 加翻译(属于用户输入数据),但 input[type="submit"] 和 button 的显示文案建议统一用 textContent 更新。
- 含 HTML 结构的文案(如“请阅读服务条款”)必须用
innerHTML替换,且语言包里对应值要是可信纯 HTML 片段(无用户输入、不执行 JS),否则有 XSS 风险 -
<select></select>的<option></option>需单独遍历处理,不能靠父级data-i18n自动推导 -
<svg></svg>内的<text></text>节点也要加data-i18n并在 JS 中识别SVGTextElement类型做特殊替换
动态插入的 DOM 必须手动触发翻译
AJAX 加载的弹窗、分页表格新行、懒加载模块插入后,data-i18n 只是字符串,不会自动变成对应语言文本。没有监听机制,也不会触发重扫描。
必须在 appendChild() 或 insertAdjacentHTML() 后立即调用翻译函数,例如:
const modal = document.createElement('div');
modal.innerHTML = '<h2 data-i18n="modal_title"></h2>';
document.body.appendChild(modal);
translateElement(modal); // 手动触发
- 使用
fetch()加载表格行后,要遍历新<tr> 里的所有 <code>[data-i18n]元素并替换 - JS 动态生成的 Toast 或 confirm 提示,不能依赖 HTML 属性,得封装
t('common_error')函数查当前语言包 - 不要指望 MutationObserver 自动处理——它无法区分哪些是翻译目标,容易误触或漏触
- 路径用模板字符串:
./locales/${lang}.json,别硬编码成./zh.json - 外层用
try/catch捕获网络错误,内部检查response.ok和response.headers.get('content-type')?.includes('application/json') - fallback 顺序:先试完整 BCP 47 码(
zh-HK),再截主语言(zh),最后退到默认语言(如内置对象{"header_title": "Welcome"}) - 所有语言包结构必须扁平、键名完全一致;某语言暂未翻译,也要保留键并设为空字符串
"btn_submit": "",避免查不到 key 就跳过
JSON 语言包加载必须带 fallback 和 MIME 校验
路径写错、网络抖动、服务器返回 404 或 text/plain MIME 类型,都会让 fetch('./locales/en-US.json') 静默失败,页面文案留空——用户看到一堆 data-i18n="xxx" 键名。
正确做法是:
语言包不能内联进 JS,也不能用 XMLHttpRequest 老式写法——构建体积膨胀、热更新困难、MIME 校验缺失,都是实际项目里踩过的坑。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











