这个笔记记录一下这个博客在国际化上的一轮调整。

最开始的问题不是“没有国际化”,是国际化初始化得不够早。

页面在首屏阶段先吐出默认语言,等浏览器启动后再从 localStorage 或语言检测器里拿到用户真正的语言,这就很容易造成语言闪动。

这个项目里最明显的表现就是:页面还没完全起来,底部导航先以中文出现,但用户设置的其实是英文。

1. 为什么“客户端再切语言”不够

国际化常见的问题链路一般是这样:

  1. 构建阶段先输出默认语言
  2. 浏览器启动后再读取用户语言
  3. 页面发生一次语言切换

这会带来几个问题:

  • 首帧语言不正确
  • navigation 或 loading 文案容易闪动
  • hydration 更容易不稳定

因此这里的关键不是“有没有切语言”,而是“首帧出来时语言是不是已经对了”。

2. 最早初始化语言

第一步做的是尽量在布局层最早初始化语言。

也就是在 head 中尽量早地执行脚本,把用户语言先写到:

  • document.documentElement.lang
  • 自定义的语言标记属性

这样做的目的是让 hydration 前后的语言上下文尽量保持一致,避免浏览器在首帧先画出错误语言。

3. 静态多语言路由才是更彻底的方案

只做“客户端切语言”还是不够。

更彻底的做法是直接生成两套路由:

  • /cn/intro
  • /en/intro
  • /cn/journey
  • /en/journey
  • /cn/knowledge
  • /en/knowledge

这样做之后,英文页在构建时产出的就是英文 HTML,中文页在构建时产出的就是中文 HTML。

它的好处是:

  • 首帧语言正确
  • SEO 更清晰
  • 页面不需要先吐默认语言再切换
  • 路由本身就带有语言语义

4. 根路径只负责语言分流

当项目已经有了明确的语言路由之后,根路径 / 最适合做一件很轻的事情:

  1. 读取用户语言偏好
  2. 显示一个很轻的 loading / redirecting 文案
  3. 跳到目标语言页面

这里还顺手处理了一个细节:如果根路径默认要跳英文,那么它的 loading 文案、脚本默认值和 noscript 回退也应该统一成英文,而不是一部分中文一部分英文。