用 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 × slug 的 generateStaticParams,便可在构建时把每个页面以每种语言预渲染出来。
静态的多语言并不比单语言更慢。它只是对调用顺序更为挑剔。
hreflang,或者说如何避免自己跟自己竞争
若不加任何提示,Google 会把你的十个语言版本,当作在相同关键词上互相争抢的十个页面。解法是 hreflang:每个页面都声明其全部变体。
两处必须保持同步:
- 每个页面的元数据 — 通过一个生成
alternate标签的generateAlternates辅助函数。 - 站点地图 — 每个 URL 在其中列出它的语言与一个
x-default。
正是这两个来源之间的一致,才让一次西班牙语搜索落到 /es 而非 /fr。
语言切换,从用户这一侧看
一个语言选择器必须在不丢失当前页面的前提下切换 locale。天真的反应——把用户送回首页——令人沮丧。借助 next-intl 本地化的 usePathname,你取到不带前缀的路径,再以所选语言把它重新发出。用户便停留在原地。
我的心得
- 根部的
[locale]段是地基;一切由它而生。 - 翻译之前先
setRequestLocale:为保住静态,这一点没有商量余地。 hreflang要成对地维护(元数据与站点地图),始终同步。- 文本方向(阿拉伯语的 RTL)在
<html dir>这一层控制,而非逐页处理。
做得好,多语言便隐于无形:每一位访客都觉得这个网站是为他而写,而 Google 也确切知道该为他打开哪一扇门。