不能只靠 navigator.language,因其返回浏览器 ui 语言而非用户真实偏好,且在安卓 webview、隐私模式、旧版 ios 下常为空或错误;正确优先级应为 url 参数 → localstorage → navigator.language → 默认语言,并需标准化为 bcp 47 格式。

直接用 navigator.language 作唯一语言来源,90% 的情况会出错——它返回的是浏览器 UI 语言,不是用户真实偏好,且在安卓 WebView、隐私模式、部分旧版 iOS 下常为空或错误值。
为什么不能只靠 navigator.language
常见错误现象:navigator.language 返回 "zh-CN",但用户实际想看繁体中文;返回 "en-US",可用户刚把系统语言切到日语却没重启浏览器;某些 Android WebView 返回空字符串,导致 fallback 到默认语言后用户完全无法切换。
-
navigator.language是浏览器界面语言,不等于网页语言偏好 - 用户可能手动改过系统语言,但未重启浏览器,
navigator.language不刷新 - 隐私模式、无痕窗口、某些嵌入式 WebView 中该值不可靠或缺失
- 返回值格式不统一:可能是
"zh-HK"、"zh-TW"、"zh",但你的语言包只认"zh-Hant"
正确优先级链:URL 参数 → localStorage → navigator.language → 默认语言
语言决策必须按明确顺序兜底,不能跳过任何一环。每层都需标准化(如转成 "zh-Hans" 或 "ja-JP"),否则下游解析会失败。
- 先检查 URL 中的
lang参数:比如?lang=ja-JP,优先级最高,方便测试和分享链接 - 再读
localStorage.getItem('preferred-lang'),这是用户上次手动选择的结果 - 然后取
navigator.language || navigator.userLanguage(后者兼容 IE),并做标准化转换(如"zh-CN"→"zh-Hans","zh-TW"→"zh-Hant") - 最后 fallback 到硬编码默认值,如
"en"或"en-US",不能留空
标准化语言码时容易忽略的细节
不校验格式就直接赋给 document.documentElement.lang,会导致屏幕阅读器、自动翻译、字体回退全部失效。
- 必须符合 BCP 47:小写字母 + 连字符 + 地区/变体码,
"zh-Hans"✅,"zh_hans"❌,"Chinese"❌ -
"ja"不够稳妥,iOS VoiceOver 某些版本会降级为英语发音,应强制补全为"ja-JP" - 简体/繁体必须区分:
"zh-Hans"和"zh-Hant"触发不同的拼音标注、标点间距、字体 fallback 链 - 若后端返回
"zh_cn",前端必须清洗:用.replace('_', '-').toLowerCase()再校验是否在白名单中
切换语言时如何让 documentElement.lang 真正生效
只设 document.documentElement.lang = "zh-Hans",对已渲染的 DOM 节点无效——浏览器和辅助技术按每个节点自己的 lang 属性判断,不是继承来的。
- 页面初始化时,必须同步设置
document.documentElement.lang和所有显式声明语言的子节点(如<blockquote lang="fr"></blockquote>) - 切换语言后,不仅要更新文案,还得遍历所有带
lang属性的元素,重写其值(除非你确定它们是“固定不变”的局部内容) - 表单控件、
<code>、<pre class="brush:php;toolbar:false;"></pre>里的代码语言标识(如lang="bash")不能被主语言覆盖,得单独保留 - 如果用了
data-i18n方案,记得刷新完文本后,再统一执行一次document.documentElement.lang = newLang
最易被忽略的一点:语言切换后,Intl.DateTimeFormat 和 Intl.NumberFormat 实例不会自动更新,必须重建;否则日期/数字仍按旧语言格式显示,用户根本意识不到语言已切换。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











