返回博客

Next.js 国际化的 5 个坑:从 React state 到 next-intl

Siwoer发布:更新:

复盘 Next.js 中英双语网站重构的五个问题:语言状态、路由改造、canonical 与 hreflang、语言切换和旧版示例,并给出排查顺序。

8 min read · 1585 words

我最初以为 Next.js 国际化只是“准备两份翻译文件,再加一个语言按钮”。真正把个人网站从中文改造成中英双语后,我才发现:最难的不是翻译,而是路由、内容组织和 SEO。

这篇文章不重复安装步骤,而是复盘我从错误方案迁移到 next-intl 的过程,以及几个看似能运行、长期却会出问题的选择。

如果你需要从零配置,请直接看《Next.js 国际化教程:用 next-intl 搭建 App Router 多语言网站》。

我的需求其实很简单

  • 支持中文和英文
  • 博客拥有独立语言内容
  • 页面结构保持一致
  • URL 可以分享、刷新和被搜索引擎收录

问题在于,我一开始只关注了第一项。

坑一:用 React state 切换文案

最早的做法是在客户端保存 locale,点击按钮后重新渲染:

保存 locale → 切换 state → 渲染另一份文案

视觉上完全正常,但 /blog/post 始终是同一个 URL。结果是:

  • 刷新后可能丢失语言状态
  • 分享链接无法表达当前语言
  • 服务端与客户端容易出现状态差异
  • 搜索引擎没有两个稳定入口可抓取
能切换文字,不等于完成了国际化。

真正适合公开内容的结构应当把语言放进路由:

/zh/blog/post
/en/blog/post

坑二:先写完网站,再改语言路由

网站只有三页时,把 /blog/[slug] 改成 /[locale]/blog/[slug] 看起来不难。实际改动会扩散到导航、重定向、动态路由、metadata、sitemap、内容目录和内部链接。

我最后不得不逐层迁移:

开始
网站只有中文,使用普通 App Router 路由。
临时方案
用客户端 state 切换翻译内容。
问题出现
分享、刷新、SEO 和内容维护逐渐失控。
重构
引入 app/[locale] 与 next-intl,统一导航和内容路径。
现在
每种语言拥有独立 URL,并在 metadata 与 sitemap 中互相关联。

如果项目未来大概率会支持多语言,语言 URL 应在第一天决定。

坑三:以为独立 URL 就等于 SEO 完成

有了 /zh/en 只是第一步。搜索引擎还需要一致的信号:

  • 当前页面的 canonical 指向自己
  • 对应语言页通过 hreflang 互相声明
  • title、description、正文与页面语言一致
  • sitemap 收录所有可索引语言 URL
  • 语言切换链接可以被正常访问

还要注意:两个语言页如果并不互为翻译,就不应该为了“完整”而强行关联。

坑四:语言切换总是跳回首页

早期切换器只会把用户从任何页面送到 /en/zh。用户在读文章时切换语言,却突然回到首页,体验非常割裂。

更合理的方式是保留当前 pathname,只替换 locale:

/zh/blog/nextjs-intl-i18n
                 ↓
/en/blog/nextjs-intl-i18n

如果目标语言没有对应内容,则应明确回退到博客列表或显示提示,而不是制造一个 404 链接。

坑五:照搬旧版或 Pages Router 示例

网上不少代码仍在使用旧路由体系,例如给普通 next/linklocale,或在嵌套 layout 里重复输出 <html>。代码可能看起来合理,却不适用于当前 App Router。

我的判断原则变成了:

  1. 先确认示例针对 App Router
  2. 核对 Next.js 与 next-intl 的版本
  3. 优先查官方文档中的当前 API
  4. 在无缓存的直接访问、刷新和构建环境中测试

为什么最后选择 next-intl

路由就是语言状态

locale 来自 URL,不需要在多个组件之间同步一份全局语言 state。

同时适配服务端与客户端组件

正文可以在服务端完成翻译,交互组件再使用客户端 Hook,不必为了国际化把整页变成 Client Component。

导航规则可以集中管理

通过 routing 与 navigation 封装,Link、redirect、pathname 和语言切换使用同一套 locale 规则,减少字符串拼接。

便于补全 SEO 信号

next-intl 不会自动让排名变好,但稳定的语言路由让 canonical、hreflang 和 sitemap 有了可靠基础。

重构后我会检查什么

  • 直接打开任意 /zh/en 深层链接是否正常
  • 刷新后语言是否保持
  • 不合法 locale 是否正确 404
  • 切换语言是否保留当前页面
  • metadata 是否与当前正文语言一致
  • canonical、hreflang 与 sitemap 是否互相一致
  • 缺少翻译或对应文章时是否有明确处理

总结

这次重构让我真正理解了一句话:国际化首先是信息架构问题,然后才是翻译问题。

next-intl 解决了很多实现细节,但最重要的工作仍然是提前设计语言 URL、内容对应关系和索引策略。只要这三件事清楚,后面的翻译和组件开发反而会简单得多。

遇到问题时,我会按什么顺序排查?

表现先检查什么验证方式
切换后刷新又变回原语言locale 是否只存在 React state复制当前 URL,在新窗口直接打开
文章切换语言后回首页切换器是否丢弃 pathname在文章详情页切换,而不是只测首页
页面出现翻译 key消息文件、命名空间、Provider核对同一 key 在两种语言中是否存在
两种语言 canonical 相同是否继承了父布局的首页元数据检查每篇文章最终 HTML 的 canonical
链接正常但没有收录响应码、robots、canonical 和内容用 GSC URL 检查查看抓取与索引状态

前四项属于实现排查;最后一项不能靠“装好 next-intl”解决,也不能仅凭没有收录就断定是路由错误。

官方参考与下一步

常见问题

只用 React state 切换语言有什么问题?
页面虽然能切换文案,但 URL 不变,刷新与分享难以保留语言,搜索引擎也无法稳定识别不同语言版本。
Next.js 国际化最先应该设计什么?
应先确定语言 URL、内容对应关系和默认语言策略,再选择翻译工具。翻译文件反而不是最难的部分。
next-intl 能自动解决所有多语言 SEO 问题吗?
不能。它能帮助建立语言路由,但 canonical、hreflang、sitemap、元数据和内容质量仍需要正确配置。
Next.js 国际化的 5 个坑:从 React state 到 next-intl