国际化需注意细节:data-i18n标记必须覆盖alt、title、aria-label、placeholder等属性;JSON语言包加载失败时应降级加载en.json或默认对象;切换语言时需同步所有lang属性并更新动态DOM;外部文本注入需用textContent,含结构文案需经DOMPurify过滤防XSS。
国际化这事儿,说到底,看着简单,真要在页面上跑得顺、不出错,其实到处是细节。很多团队把翻译文件一丢、切换按钮一挂就觉得完事了,结果上线后问题一个接一个:有地方没翻译到、切换时页面闪一下、读屏软件读出奇怪的东西来——今天就挨个把这些“坑”挑明了说。

长期稳定更新的攒劲资源: >>>点此立即查看<<<
只给 加个属性,转头把 给忘了,结果切换语言后那个 placeholder 纹丝不动,还是英文——这不是漏翻译,是标记根本没打全。
常见的“漏网之鱼”有哪些?alt、title、aria-label、placeholder,这些属性靠 textContent 更新逻辑是触达不到的。必须为它们单独加后缀:
data-i18n-placeholder="search_hint"data-i18n-alt="user_a vatar"data-i18n-title="tooltip_info"data-i18n-aria-label="close_button"至于 value 属性,一般不需要动,那是用户输入的数据。但 和 这种带显示文案的,最好还是用 textContent 来更新,别不小心把英文值给提交上去了。
fetch() 去加载 ./locales/zh.json,结果网络一抖没拉下来,或者某个 key 在 zh.json 里压根没定义——比如 "na v_home" 缺失。默认行为就是留空,用户看到的不是“未翻译”,而是白板一块。
怎么办?加载和查找两个环节都得兜底:
try/catch,失败了就降级去加载 en.json,或者直接给一个内置的默认对象langPack[key] langPack.fallback "MISSING_KEY",不能直愣愣地写 langPack[key],否则查不到就是 undefined"btn_submit": "",至少结构不能缺Content-Type 必须是 application/json,不然 response.json() 会静默抛错,你都不知道问题出在哪很多人只写了 document.documentElement.lang = "zh-Hans",却忘了页面里可能还有 或 。结果呢?屏幕阅读器依然按旧的 lang 朗读,中文顿号的停顿全乱了,英文字体回退也不生效——不是 JS 没跑完,是浏览器根本不认根节点继承这套逻辑。
切换语言时,必须做两件事:
lang 属性的元素(document.querySelectorAll("[lang]")),除了那些明确要保留原语言的(比如 ),其余一律改成当前语言码data-i18n 标记只是个普通字符串,不会自动生效zh-Hans 而非 zh_CN 或 chinese,否则浏览器会直接忽略,甚至触发 fallback 失败举个例子:用 加载一个文本文件,里面是变量声明,然后把这个内容塞进 。如果这个文本来自 CMS 或日志生成,而语言包里又恰好用了 innerHTML 渲染带链接的文案(比如 "terms_link": "请阅读服务条款"),XSS 就藏在这条链路的中间。
安全边界必须划清楚:
textContent 注入,不许走 HTML 解析那条路 或链接的)必须走 innerHTML,条件是语言包对应的值必须是可信的纯 HTML 片段——不能拼接用户输入,不能执行 JSDOMPurify.sanitize(),就算来源是内部系统也别掉以轻心 和 内部不要写 data-i18n——它们不参与渲染,JS 替换无效,还可能被误解析最容易被忽略的其实是:语言包里的 HTML 片段一旦包含了用户可控内容(比如评论摘要),就必须过滤;而外部 .txt 文件如果被攻击者篡改,let text = '...' 就成了执行入口。这两条链路的安全水平,必须保持一致。