HTML国际化需全链路工程约束:严格遵循BCP 47规范设置lang属性,显式声明各文本节点语言,同步更新动态插入DOM的lang与data-i18n属性,语言包扁平化管理并fallback,第三方组件需重建locale实例。

HTML国际化不是“加个lang属性就完事”,而是从结构、标记、加载、更新到渲染全链路的工程约束。不满足BCP 47、不覆盖动态属性、不显式声明子元素lang,等于没做。
lang属性必须严格按BCP 47写,且不能只设在上
浏览器和屏幕阅读器不看根节点lang是否正确,只认每个文本节点自身的lang值。写lang="zh"或lang="en-us"会被忽略,必须用lang="zh-CN"、lang="en-US"等标准格式。
- 所有含文本的语义化标签(
<p>、<h1>、<section>、<footer>)都要显式带lang,值与当前语言包一致 - 已有特殊语言的元素(如
<pre lang="bash">、<code lang="sql">)保留原lang,这是合法混排,别强行覆盖 -
<script>和<style>里写lang无效,直接删掉 - 切换语言时,要遍历并同步更新所有子元素的
lang,不能只改document.documentElement.lang
data-i18n只管textContent,placeholder/alt/title等必须显式后缀
data-i18n本身不会动任何属性——它只替换元素的textContent。如果你有<input placeholder="Search">,切换语言后placeholder还是英文,因为没人告诉JS去更新它。
- 需要翻译的属性,必须加对应后缀:
data-i18n-placeholder、data-i18n-alt、data-i18n-title、data-i18n-aria-label -
value一般不翻译(属于用户输入数据),但<button>和<input type="submit">建议用textContent更新文案,避免提交value中的原始字符串 - 含HTML结构的文案(如“请阅读服务条款”)要用
innerHTML替换,且语言包里的值必须是可信纯HTML片段,不能拼接用户输入 - 别给
<script>或<pre>加data-i18n——它们不渲染为可见文本,加了也不触发替换
JSON语言包结构必须扁平、键名对齐、带fallback
语言包不是随便堆键值对的地方。结构混乱或缺失key,会导致整页文案留空,而且很难定位问题。
立即学习“前端免费学习笔记(深入)”;
- 每个语言一个文件:
locales/zh.json、locales/en.json、locales/ja.json,内容全是顶层键值对,不嵌套 - 所有文件键名必须完全一致;某语言暂未翻译,也要留
"home.title": ""或占位符,否则JS查不到key就留白 -
fetch()加载失败、key不存在、网络超时,都必须fallback到主语言(如en),不能让页面变空白 - 模块化拆分可选(如
common.json、form.json),但需用Promise.all并行加载,避免串行阻塞
动态插入的DOM必须手动触发翻译,MutationObserver靠不住
AJAX加载的弹窗、分页表格新行、懒加载模块……这些节点插入后,data-i18n只是字符串,不会自动变成对应语言文本。等它自己反应?不存在的。
- 弹窗打开后,要立即调用翻译函数遍历其内部所有
data-i18n元素 - 分页获取新
<tr>后,在appendChild之后立刻执行翻译逻辑,不能依赖全局监听 - 不要用
MutationObserver自动监听——它无法识别哪些节点该翻译、哪些是代码块或用户输入,容易误翻或漏翻 - 第三方组件(如日期选择器)的语言切换,必须调用其API重建实例(如
Intl.DateTimeFormat('zh-CN')),复用旧对象会沿用旧locale
最容易被忽略的是:语言切换不是文本替换,而是一次完整的渲染上下文重置。标点间距、字体回退链、语音朗读方式、RTL方向、甚至表单验证提示,全都依赖每个节点自己的lang和dir。漏掉任意一环,用户看到的就不是“多语言”,而是“错位的中文页面”。



















