必须归一化 navigator.language:转小写、取主语言码、过滤无效值;应在 中首个同步脚本执行,避免闪动;优先用 navigator.languages[0] 降级处理;SSR 需用 Accept-Language 头;简繁需按 ["zh-CN","zh-Hans","zh"] 顺序匹配。
直接读
会匹配失败?先做归一化
浏览器返回的
值不规范:可能是
、
、
,甚至旧版 Safari 返回全小写,IE 可能 fallback 到
。直接拿它当 key 去查
几乎必挂。
必须提前归一化:
统一转小写:
取主语言码:
(得到
、
、
)
过滤无效值:
为什么不能等
或框架初始化后再读?
DOM 渲染后才挂载翻译,会导致文本闪动:先显示英文(或占位符),再刷成中文。用户感知明显,尤其在慢网或低端设备上。
正确时机是 HTML 的
中第一个非
/
的
:
把归一化逻辑放这里,立即计算出
i18next 初始化时直接传
自定义方案也用这个值索引
比
更可靠吗?
是的,但要用对。它返回数组,如
,按用户设置优先级排序。比单值更全面,但不是所有浏览器都支持(IE 全线不支持)。
稳妥做法是降级使用:
优先用
回退到
再不行用
最后 fallback 到硬编码默认值(如
)
注意:数组元素本身仍需归一化,不能跳过大小写和子标签处理。
服务端渲染(SSR)下
根本不存在
Node.js 环境没有
对象,SSR 渲染时若直接引用会报
。
必须拆分逻辑:
服务端靠请求头
解析首选语言(用
这类轻量库)
客户端首次 hydration 时,用
做二次校准(比如用户改了系统语言但没刷新页面)
避免“服务端渲染中文 → 客户端重绘英文”这种错乱
真正容易被忽略的是地区语义:你有
和
包,但只按
匹配,就丢掉了简繁区分能力。匹配策略得按
顺序尝试,而不是一刀切。
navigator.languagenavigator.language"zh-CN""zh-tw""en-us"navigator.userLanguagemessages[lang]lang = (navigator.language || navigator.userLanguage || 'en').toLowerCase()const baseLang = lang.split('-')[0]"zh""en""fr"if (!baseLang || baseLang === 'und' || baseLang.length !== 2) baseLang = 'en'window.onloadasyncdeferwindow.__INITIAL_LOCALE__lng: window.__INITIAL_LOCALE__messages[window.__INITIAL_LOCALE__]navigator.languagesnavigator.language["zh-CN", "zh", "en-US", "en"]navigator.languages?.[0]navigator.languagenavigator.userLanguage'en'navigator.languagenavigatorReferenceError: navigator is not definedAccept-Languageaccept-languagenavigator.language"zh-Hans""zh-Hant""zh"["zh-CN", "zh-Hans", "zh"]