data-i18n 必须显式标注所有可翻译属性(如 placeholder、alt),lang 需逐层设置且符合 bcp 47,组件 key 应加命名空间前缀,动态 dom 需手动触发翻译并隔离项目语言状态。

data-i18n 标记必须覆盖所有可翻译属性,不只是 textContent
很多团队只给 <p data-i18n="welcome"></p> 加标记,结果切换语言后 <input placeholder="Search"> 还是英文,<img alt="logo"> 的替代文本也没变——因为 data-i18n 默认只改 textContent,对 placeholder、alt、title、aria-label 等属性完全无效。
必须显式加后缀:
data-i18n-placeholder="search_hint"data-i18n-alt="logo_desc"data-i18n-title="tooltip_info"data-i18n-aria-label="close_btn"
按钮和提交类输入控件(<button></button>、<input type="submit">)建议统一用 textContent 更新文案,避免依赖 value 属性——后者可能被表单提交,且不参与语义化朗读。
lang 属性不能只设在根节点,必须逐层显式声明
只执行 document.documentElement.lang = 'zh-Hans' 是无效操作:已渲染的 <p></p>、<h2></h2>、<footer></footer> 仍保持原 lang 值,导致标点间距错乱、字体回退失效、屏幕阅读器读错音调。
正确做法是同步更新所有含文本的语义化元素:
- 每个
<h1></h1>、<p></p>、<section></section>、<article></article>都需带lang="zh-Hans" - 已有特殊语言内容(如
<pre class="brush:php;toolbar:false;" lang="bash"></pre>、<code lang="sql">)保留原值,这是合法混排场景,不要覆盖 -
<script></script>和<style></style>内部禁止写lang,它们不参与文本渲染
BCP 47 格式必须严格遵守:zh-Hans 可以,zh_CN 或 chinese 会被浏览器忽略,甚至触发 fallback 失败。
组件模板内必须隔离语言逻辑,避免跨项目 key 冲突
纯 HTML 组件库(如 <template data-component="c-modal"></template>)若直接硬编码键名(data-i18n="confirm_btn"),一旦多个项目共用该模板,语言包 key 极易重复或缺失。
安全做法是为组件定义命名空间前缀:
- 组件级 key 写成
c-modal-confirm、c-table-empty-state - 语言包结构扁平,不嵌套:
{"c-modal-confirm": "确认", "c-table-empty-state": "暂无数据"} - 加载时按组件 ID 动态拼接前缀,避免全局 key 泄漏
动态插入的 DOM(如 AJAX 加载的表格行、弹窗内容)不会自动翻译,必须手动触发翻译函数,并确保新节点也带上正确的 lang 和 data-i18n 属性——MutationObserver 不可靠,别依赖它。
复用组件的国际化状态不能靠 localStorage 全局共享
多个项目共用同一套组件时,如果都读写同一个 localStorage key(如 i18n-lang),会导致语言切换互相干扰。
解决方案是绑定项目上下文:
- 语言存储 key 加项目前缀:
my-app-i18n-lang、admin-panel-i18n-lang - 组件初始化时传入项目标识,用于隔离语言包加载路径和存储域
- fallback 机制按项目独立配置,不能共用一个默认语言(比如 A 项目 fallback 到 en,B 项目 fallback 到 ja)
JSON 语言包路径也需项目隔离:/locales/my-app/zh.json vs /locales/admin-panel/zh.json,否则 key 冲突或加载错文件。
lang 属性继承断裂和动态 DOM 的语言状态脱钩——这两处出问题,用户看到的是文字变了但标点挤在一起、语音朗读全是英文腔,而开发者调试时根本看不出 DOM 上少了什么。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











