最轻量可行路径是data-i18n标记+JSON语言包+localStorage持久化;需显式为所有文本节点及placeholder/title/alt等属性添加对应后缀,语言包扁平结构、BCP 47命名,切换时同步更新document.documentElement.lang、localStorage及动态节点。

data-i18n 属性怎么加才不漏文本
只给 <button> 加 data-i18n,但漏掉 placeholder、alt、title 或 aria-label,切换语言后这些字段还是英文——这不是 bug,是标记没打全。
-
<input placeholder="Search">必须同时加data-i18n-placeholder="search_hint" -
<img alt="User avatar">必须加data-i18n-alt="avatar_desc" -
<label for="email">Email</label>要加data-i18n,且for值必须与目标id严格一致,否则点击 label 失效 -
value属性一般不翻译(属于用户输入数据),但<button type="submit">的显示文案建议统一走textContent更新,避免意外提交原始键名 - 含 HTML 结构的文案(如“请阅读服务条款”)必须用
innerHTML替换,且语言包里对应值要是可信纯 HTML 片段(不能拼接用户输入,也不执行 JS)
lang 属性为什么设了根节点却没用
设了 document.documentElement.lang = 'zh-CN',但 <p> 里的顿号间距还是英文、<pre lang="bash"> 字体被中文字体覆盖、屏幕阅读器仍读英文——因为浏览器和辅助技术完全不继承 <html> 的 lang,它们只看每个元素自身的 lang 属性。
- 所有含文本的语义化标签(
<h1>、<p>、<section>、<footer>等)都必须显式写lang="zh-CN" - 已有
lang的特殊元素(如<pre lang="bash">、<code lang="sql">)切换语言时保留原值,这是合法混排,不是错误 -
<script>和<style>内部加lang没意义,不参与渲染 -
<title>和<meta name="description">完全不继承根节点lang,必须单独更新
JSON 语言包加载失败怎么兜住
fetch 失败或返回空对象,结果页面大片留白或显示原始键名(如 "nav_home"),用户看到的是“错误”,不是“正在加载”——这是因为没做 fallback 校验,JS 查不到 key 就直接返回 undefined。
- 路径统一为
./locales/${lang}.json,例如./locales/zh-HK.json、./locales/en-US.json - 必须用
fetch()+try/catch包住整个流程,捕获TypeError: Failed to fetch和 404 - 成功响应后,检查
response.ok和response.headers.get('content-type')?.includes('application/json'),避免 MIME 类型错误导致静默失败 - fallback 顺序:先试完整 BCP 47 码(
zh-HK),再截主语言(zh),最后退到硬编码默认对象(如{ login_btn: "Login" }) - 语言包结构必须扁平,所有文件键名对齐;某语言暂未翻译也要保留键,设为空字符串
"btn_submit": ""或占位符"btn_submit": "[未翻译]"
动态插入的 DOM 怎么自动翻译
AJAX 加载弹窗、分页表格新行、懒加载模块插入后,里面带 data-i18n 的节点仍是原始键名——因为 DOM 插入是异步的,没人告诉翻译函数“新节点来了”。这不是监听问题,是调用时机问题。
立即学习“前端免费学习笔记(深入)”;
- 每次插入新 DOM 后,立即手动调用翻译函数(如
translateNode(modalEl)或translateChildren(newTr)) - 不要依赖 MutationObserver 全局监听——它无法区分哪些节点该翻、哪些不该翻(比如
<pre lang="bash">里的文本不该动) - 弹窗打开后、表格行 append 后、Tab 切换新面板后,都是明确的触发点,就在这儿调
- 翻译函数内部应只处理带
data-i18n的元素,跳过已处理过的(可用data-i18n-done标记防重入)
最易被忽略的点:语言切换时,document.documentElement.lang 改了,但所有已有 lang 属性的元素(比如 <p lang="en">)根本不会自动同步。必须主动遍历并更新——除非你明确想保留某段内容的原语言(如代码块、引文),否则它们会继续按旧语言渲染标点、字体和语音。



















