JavaScript多语言支持应避免手写if判断,主流方案是用i18n库管理翻译资源并动态切换;核心难点在于翻译键的规范管理、不散落、不冲突、不漏译、不阻塞渲染。

JavaScript 多语言支持不靠手写 if (lang === 'zh') {...},主流方案是用 i18n 库管理翻译资源 + 运行时动态切换。核心难点不在“怎么加语言”,而在“怎么让翻译键不散落、不冲突、不漏译、不阻塞渲染”。
选库前先看你的构建环境和更新频率
不同项目对 i18n 的要求差异极大:
- 纯静态站点(如文档页):用
i18next+ JSON 文件最稳,支持编译时预加载,也兼容 CDN 直接引入 - Vite / Webpack 项目:推荐
@inlang/paraglide-js,它把翻译键变成类型安全的函数调用,比如$t('header.title'),IDE 能跳转、TS 能校验、漏译会报错 - React 项目:别重复造轮子,直接用
react-i18next(基于 i18next),它的useTranslationHook 自动订阅语言变化,且支持按组件拆分 namespace,避免全量加载翻译包 - 需要后端同步语言状态:选支持 backend plugin 的库(如 i18next 的
i18next-http-backend),它能按需从/locales/en/common.json动态拉取,但要注意缓存头和 404 处理
i18next 初始化必须避开的三个坑
很多项目卡在初始化就白屏或翻译不生效,问题通常出在加载时机和配置细节:
- 不要在
i18next.init()前调用t('key')—— 它默认异步加载,得等ready回调或 awaiti18next.ready -
fallbackLng必须显式设为数组,比如['en', 'dev'],不能只写'en',否则非 fallback 语言缺失 key 时会静默失败 - 如果用
resources内联翻译,确保结构是{ en: { translation: { ... } } },少一层translation就会导致所有t()返回 key 字符串本身
import i18next from 'i18next';
i18next.init({
lng: 'zh',
fallbackLng: ['en', 'dev'],
resources: {
zh: { translation: { welcome: '欢迎' } },
en: { translation: { welcome: 'Welcome' } }
}
});
翻译键设计比选库更重要
再好的库也救不了乱命名的 key。实际协作中,90% 的维护成本来自键名歧义和层级混乱:
立即学习“Java免费学习笔记(深入)”;
- 用点号分层,但层级不超过 3 层:
form.submit.button可以,page.dashboard.card.header.action.confirm.text就难维护 - 避免用中文或带空格的 key:
'用户登录成功'看似直观,但换语言后无法保证语序一致,且 IDE 搜索困难;应统一用英文小写+下划线:auth.login_success - 带变量的句子必须用插值,而不是拼字符串:
t('search.result_count', { count: 12 }),对应 JSON 中"search.result_count": "找到 {{count}} 条结果"—— 否则日语、阿拉伯语等语序颠倒的语言会翻错
服务端渲染(SSR)场景下语言检测要早于 React 渲染
Next.js 或 Nuxt 项目里,getServerSideProps 或 useEffect 里切语言是常见错误——页面已渲染完成才改语言,导致闪屏或两次请求。
- Next.js App Router:在
layout.tsx的generateStaticParams或generateDynamicParams阶段就确定lng,并通过cookies().get('NEXT_LOCALE')或headers().get('accept-language')提前读取 - 必须把 i18n 实例挂到 request 上(如 Next.js 的
createInstance+initSSR),确保服务端和客户端共享同一份实例,否则 hydration 会失败 - 避免在
useEffect里调用i18next.changeLanguage(),它触发重渲染,而 SSR 已输出 HTML —— 正确做法是让路由路径带语言前缀(/zh/login),由服务端根据 path 初始化语言
真正麻烦的不是加多语言,而是当产品迭代新增 5 个页面、3 种语言、2 个地区变体(en-US / en-GB)时,还能快速定位某个按钮文案漏译了哪一版。这时候,键的命名规范、CI 里的 JSON 格式校验、以及是否启用 missing key 上报(如 i18next 的 saveMissing + 自定义 backend),比库本身重要得多。











