data-i18n 必须显式打在每个可翻译节点上,placeholder、title 等属性需用 data-i18n-placeholder 等后缀标记;动态 DOM 插入后须立即调用翻译函数;语言包须扁平化、键名一致、fallback 完整;lang 属性需逐元素同步;组件模板禁止硬编码文案,样式须用 CSS 自定义属性控制。

data-i18n 必须显式打在每个可翻译节点上
不加 data-i18n 的元素,哪怕在语言包里有对应键,也不会被翻译函数识别。常见漏点包括:placeholder、title、alt、value 这类属性——它们不会被 textContent 覆盖,必须用带后缀的标记,比如 data-i18n-placeholder="form.email"。
以下写法无效:
<input placeholder="邮箱地址">
正确写法是:
<input data-i18n-placeholder="form.email" placeholder="邮箱地址">
动态插入的 DOM(如弹窗、表格行、AJAX 返回内容)插入后必须立即调用翻译函数,否则这些节点永远不会被处理。
立即学习“前端免费学习笔记(深入)”;
- 不要依赖“全局扫描一次就完事”的假设;新节点需手动触发翻译
-
<script>、<style>、<pre>内部不渲染文本,加data-i18n没效果 - 含 HTML 结构的文案(如“请阅读 服务条款”)要用
innerHTML替换,但语言包值必须是可信纯 HTML,否则有 XSS 风险
JSON 语言包必须扁平 + 键名严格一致 + fallback 链完整
语言包结构嵌套(如 {"ui": {"header": {"title": "Home"}})会导致 JS 查找逻辑复杂、易出错,且无法与 data-i18n="ui.header.title" 简单映射。所有语言文件应为顶层键值对,例如:
{ "header_title": "Home", "form_email_required": "Email is required" }
加载失败时页面空白,往往是因为没做 fallback:fetch 失败、响应非 JSON、或 key 缺失都会导致查不到值。
- 路径用
./locales/${lang}.json,别硬编码成./zh.json - fallback 顺序:先试完整码(
zh-HK),再截主语言(zh),最后退到默认(en) - 某语言暂未翻译,也要保留键并设为空字符串:
"btn_submit": "",否则 JS 会跳过该节点 - 服务器返回 JSON 时 MIME 类型必须是
application/json,否则fetch()可能静默失败
lang 属性必须逐元素同步,不能只改 documentElement
只设置 document.documentElement.lang = 'zh-Hans',对已存在的子元素完全无效。屏幕阅读器、字体回退、标点间距等都按每个节点自身的 lang 属性判断,不是继承来的。
切换语言后,必须遍历所有已有 lang 属性的元素,并更新其值,除非明确要保留局部语言(如代码块、引文):
- 页面加载时,
document.documentElement.lang必须设为 BCP 47 标准码(zh-Hans、ja),zh_CN或chinese会被忽略 - 局部多语言内容(如
<p lang="ja">…</p>)必须显式保留lang,不能被全局切换覆盖 - 动态插入的节点,若需继承主语言,插入后要立刻设
node.lang = currentLang
组件模板内禁止硬编码文案,所有文本走 data-i18n + CSS 变量控制样式
把 “提交” 写死在 <button>提交</button> 里,等于放弃组件复用能力。组件库中的每个可实例化模板(如 <template id="c-button">)必须只含占位标记:
<button data-i18n="btn.submit"></button>
同时,所有尺寸、颜色、圆角必须抽成 CSS 自定义属性,禁用 px 和 !important:
:root { --c-space-md: 1rem; --c-color-text: https://www.php.cn/link/93ac0c50dd620dc7b88e5fe05c70e15b333; }
这样主题切换、高 DPR 屏适配、无障碍缩放才真正可控。
- 模板内禁止出现
id="btn-1"—— 多次实例化会导致 ID 重复,document.getElementById()行为不可预期 -
<template>中的<script>和<style>默认不生效,需 JS 手动提取并插入 - 填充数据必须手动遍历
querySelectorAll('[data-bind]')或类似机制,不能靠 class 名隐式绑定
data-component、<template>、CSS 自定义属性是强耦合的。漏掉任意一环,比如没同步子元素 lang,或模板里写了死文案,后续维护就会变成修修补补的体力活。



















