跳到主要内容

用 next-intl 打造真正多语言的 Next.js 网站

本地化路由、静态渲染、hreflang 与语言切换:我如何在不牺牲性能与 SEO 的前提下,用十种语言构建一个 Next.js 网站。

为什么多语言是一个真正的架构问题

翻译一个界面很容易。而做一个像样的多语言网站——可被索引、快速、可维护——是一个在开头、而非结尾才做的架构决策。这个作品集存在于十种语言之中,以下是那些能在规模上站得住的选择。

以 locale 段进行路由

在 App Router 下最稳健的方式:在根部放一个动态的 [locale] 段。每个页面都位于 app/[locale]/… 之下,语言由此成为一等的 URL 数据(/fr/projects/en/projects)。

next-intl 中,这个段会在渲染时被校验:

  • 受支持的 locale 列在一个中央的 routing 配置里。
  • 未知的 locale 会触发一个干净的 notFound(),而不是一个坏掉的页面。
  • 导航经由一个本地化的 Link,它会自动为正确的语言加上前缀。

好处在于:组件从不「手动」摆弄 locale。它们请求一段翻译,其余交给系统。

静态渲染的陷阱

这是在生产环境里代价最高的错误。如果你在没有先固定 locale 的情况下调用翻译,next-intl 会去读取请求头。结果:页面翻转为动态渲染,你便失去了静态预渲染——有时还伴随生产环境里的 DYNAMIC_SERVER_USAGE 报错。

对策只需一行,就在每个页面的最顶部:

setRequestLocale(locale);

在任何 getTranslations 之前调用,它就能保证完全的静态渲染。再配合一个交叉 locale × sluggenerateStaticParams,便可在构建时把每个页面以每种语言预渲染出来。

静态的多语言并不比单语言更慢。它只是对调用顺序更为挑剔。

hreflang,或者说如何避免自己跟自己竞争

若不加任何提示,Google 会把你的十个语言版本,当作在相同关键词上互相争抢的十个页面。解法是 hreflang:每个页面都声明其全部变体。

两处必须保持同步:

  • 每个页面的元数据 — 通过一个生成 alternate 标签的 generateAlternates 辅助函数。
  • 站点地图 — 每个 URL 在其中列出它的语言与一个 x-default

正是这两个来源之间的一致,才让一次西班牙语搜索落到 /es 而非 /fr

语言切换,从用户这一侧看

一个语言选择器必须在不丢失当前页面的前提下切换 locale。天真的反应——把用户送回首页——令人沮丧。借助 next-intl 本地化的 usePathname,你取到不带前缀的路径,再以所选语言把它重新发出。用户便停留在原地。

我的心得

  • 根部的 [locale] 段是地基;一切由它而生。
  • 翻译之前先 setRequestLocale:为保住静态,这一点没有商量余地。
  • hreflang 要成对地维护(元数据站点地图),始终同步。
  • 文本方向(阿拉伯语的 RTL)在 <html dir> 这一层控制,而非逐页处理。

做得好,多语言便隐于无形:每一位访客都觉得这个网站是为他而写,而 Google 也确切知道该为他打开哪一扇门。

AGENTS.md 与代码智能体:开发者的新工具