data-i18n标记需覆盖placeholder、title、alt、aria-label等所有可翻译属性,JSON语言包必须扁平且键名严格对齐,切换语言时须同步更新document.documentElement.lang及各元素lang属性,并分层加载语言包以保障fallback可靠性。

data-i18n标记必须覆盖所有可翻译属性,不只是textContent
只给元素加data-i18n,文字能翻,但placeholder、title、alt、aria-label全保持原样——用户点输入框还是看到英文提示,图片替代文本不读中文,tooltip也不变。这不是国际化失效,是标记漏了。
实际操作时得按属性显式加后缀:
<input data-i18n-placeholder="search_hint"><img data-i18n-alt="user_avatar" alt="User avatar"><button data-i18n-title="btn_delete_tooltip">删除</button>
注意:value属性一般不翻译(属于用户输入数据),但button和input[type="submit"]的显示文案建议统一用textContent更新,避免提交时意外发错值。
JSON语言包结构必须扁平且键名严格对齐
嵌套结构看着清晰,但会导致切换语言时查不到key:比如en.json里是{"form": {"email": "Email"}},而zh.json写成{"form.email": "邮箱"},JS遍历时直接留空。工程化要求所有语言包字段完全一致,且全部扁平。
立即学习“前端免费学习笔记(深入)”;
- 路径固定为
./locales/zh.json、./locales/en.json等 - 键名全小写+下划线,如
nav_home、form_submit,禁止驼峰或空格 - 某语言暂未翻译,也要保留键并设为空字符串:
"nav_home": "",否则渲染空白 - HTTP响应头必须含
Content-Type: application/json,否则fetch()可能静默失败
切换语言时必须同步更新document.documentElement.lang和所有子lang属性
只改document.documentElement.lang = 'zh-Hans',页面文字变了,但屏幕阅读器仍按旧语言朗读,中文顿号被当英文标点处理,<pre lang="bash">里的字体被中文字体覆盖——因为浏览器和辅助技术只看**每个元素自身的lang属性**,不继承根节点。
- 初始化时就该设
document.documentElement.lang,值必须符合BCP 47(zh-Hans可,zh_CN或chinese会被忽略) - 切换语言后,需遍历所有已带
lang属性的元素(如<p lang="en">、<blockquote lang="ja">),仅更新那些本应随主语言变化的节点;明确多语混排的(如代码块lang="bash")要跳过 - 动态插入的DOM(AJAX弹窗、分页表格新行)插入后必须手动调用翻译函数,MutationObserver无法可靠捕获
加载策略要兼顾首屏性能与fallback可靠性
全量预加载所有语言包,首屏体积暴涨;每次切换都fetch单个文件,弱网下卡顿明显。工程化不是选其一,而是分层加载。
- 优先检查URL参数
?lang=ja→ 再查localStorage.getItem('preferred-lang')→ 最后fallback到navigator.language(注意截取主语言码,zh-CN→zh) - 用
Promise.all并行加载当前语言+默认语言(如zh.json和en.json),确保fallback有底牌 - 加载失败必须有兜底:
try/catch包裹fetch和response.json(),key不存在时返回en对应值,绝不能留空 - 服务端应通过
Accept-Language头解析首选语言,并注入为document.documentElement.lang,避免纯前端检测在WebView或隐私模式下失灵
最易被忽略的是:动态内容(如用户生成的评论、API返回的错误提示)根本不在HTML模板里,它们需要单独对接后端i18n接口或集成机器翻译,这部分常被当作“后续优化”拖到上线后才补,结果多语言只覆盖了静态文案。



















