不能直接用navigator.language加载资源包,因其返回值如"zh-CN"含地区后缀,而语言包通常仅命名为zh.json;须先split("-")[0]提取主语言码,校验是否在支持列表中,否则按localStorage、默认语言逐级fallback。
navigator.language 能直接拿到浏览器首选语言,但直接用它加载资源包大概率会出错——因为返回值如 "zh-CN" 或 "en-US" 很可能不在你的语言列表里,也没做降级 fallback。必须先标准化、再校验、最后加载。
navigator.language 作为资源文件名浏览器返回的值带地区后缀("zh-CN"、"pt-BR"),而你实际维护的语言包大概率只叫 zh.json、pt.json;更麻烦的是,用户可能只支持 en,但浏览器返回 en-GB,这时不提取主语言码就会加载失败。
常见错误现象:fetch("lang/en-GB.json") 404,页面文本全变成 key(如 "welcome");或者加载了错误语言包导致乱码或缺失翻译。
标准做法是提取主语言码,并按优先级 fallback:
navigator.language 或 navigator.languages[0](后者更准,但 IE 不支持).split("-")[0] 提取主语言标识,如 "zh-CN" → "zh"
SUPPORTED_LANGS = ["zh", "en", "ja", "es"] 列表中"en"
fetch 加载语言包时如何避免阻塞首屏渲染直接在 DOMContentLoaded 里 await fetch(langUrl) 会让整个页面等 JSON 下载完才开始渲染,尤其弱网下明显卡顿。静默加载的关键是「异步 + 缓存 + 非阻塞」。
实操建议:
fetch(langUrl).then(r => r.json()).catch(() => ({})),失败不抛错,兜底返回空对象cache: "force-cache" 或利用 Service Worker 缓存已加载过的语言包const en = { welcome: "Welcome" }),避免白屏document.querySelectorAll("[data-i18n]") 正确更新已有 DOM很多教程只说“遍历元素设 textContent”,但忽略两个关键点:一是属性值可能是嵌套 key(如 data-i18n="form.login.button"),二是插值参数(如 { name: "Alice" })需要运行时注入。
正确处理方式:
data-i18n 值按 . 拆解,逐层取值:obj["form"]["login"]["button"]
data-i18n-params='{"name":"{username}"}',再用正则替换textContent 或 placeholder 等安全属性load 事件里批量操作,改用 MutationObserver 监听新插入节点并自动初始化用户手动切语言后写入 localStorage.setItem("lang", "ja") 很简单,但以下情况常被忽略:
navigator.language,而不是崩成 undefined
?lang=fr 的优先级应高于 localStorage(运营活动强需求),但刷新后要同步写回 localStoragestorage 事件监听)localStorage 值必须校验合法性,防止存入 "xss<script>"</script> 类非法字符串影响后续 eval 或模板渲染最易被忽略的其实是语言码标准化时机:不是在加载资源前标准化一次就完事,而是在每次调用 t("key") 时都应基于当前有效语言码查表——因为用户可能在页面生命周期内切换语言,而 JSON 包已全部加载完毕,此时只需换 lookup 行为,无需重复 fetch。