网站翻译

Next.js 应用路由器和页面路由器的国际化

Next.js 应用路由器和页面路由器的国际化
Rayne Aguilar
作者
Rayne Aguilar
Elizabeth Pokorny
审阅人
Elizabeth Pokorny
更新于
2026年6月12日

Next.js 有两份官方的 i18n 文档页面,它们描述的是完全不同的系统,且彼此之间没有任何交叉引用。

一篇介绍了 Pages Router,另一篇介绍了 App Router。但两者均未说明二者之间的关联、应使用哪些库,以及如何处理多语言 SEO 中的 hreflang 属性。

我们将实现两个路由器的桥接。我们将详细探讨路由策略、库的选择、中间件的配置、服务器端组件的翻译以及多语言SEO。

我们的目标是为那些被要求为 Next.js 应用添加语言支持、且在确定具体方案前需要全面了解情况的开发者,提供一份实用的参考资料。让我们开始吧!

Next.js 在国际化方面提供了哪些功能

国际化分为三个层次:

  • 路由,它为每个区域设置专属的URL。
  • 翻译,用于加载正确的字符串。
  • 日期、数字和货币的格式设置

Next.js 仅提供路由功能。

自 v10.0.0 版本起,Pages Router 便内置了国际化(i18n)路由功能。next.config.js文件中一个包含三个字段的配置块会自动处理语言环境检测、URL 前缀添加以及重定向。

App Router 则有所不同。其国际化(i18n)文档页面介绍了一种模式,你可以通过中间件自行实现,一种 [locale] 动态段和一个库。

无论使用哪种路由器,翻译和格式设置都需自行处理。App Router 的文档列出了所有兼容的库,但并未提供选择建议。这两份文档均未涉及 hreflang 标签或多语言网站地图。

Pages Router 与 App Router 在国际化(i18n)方面的区别

Pages Router 的实现方式只需修改配置。在next.config.js中添加一个 i18n 块,包含以下三个字段:

// next.config.js
module.exports = {  
  i18n: {    
    locales: ['en', 'fr', 'de'],    
    defaultLocale: 'en',  
  },
}

就这样。

Next.js 会自动从 接受语言 标题,遵循 NEXT_LOCALE 覆盖 Cookie,并通过 useRouter().locale. 您无需使用任何中间件或重新组织目录结构。

App Router 没有对应的配置。您需要通过以下三个部分自行构建路由:

  • proxy.ts,原名 middleware.ts,负责检测语言环境并进行重定向。它使用 @formatjs/intl-localematcher 以及 谈判代表 进行解析 接受语言 标头。官方文档中,实际的检测逻辑被省略了: function getLocale(request) { ... }. 我们将在下一节中补充说明。
  • [locale] 动态段落会包裹你的整个 app/ 目录。通过文件夹结构,每条路由都能识别语言环境。
  • 生成静态参数 在构建时预渲染每个语言环境变体。

在这三个组件中,generateStaticParams 取代了 Pages Router 在构建时自动处理的功能。

export function generateStaticParams() {  
	return [{ locale: 'en' }, { locale: 'fr' }, { locale: 'de' }]
}

🚨 Pages Router 内置的 i18n 功能无法与 输出:'export'. 如果您需要完全静态的导出,则需要改用基于文件夹的手动路由。此限制仅适用于内置配置。这两种路由器在手动配置的情况下均支持静态导出。

子路径路由、域路由以及无需修改 URL 的国际化

在选择库之前,先确定路由策略。这会影响 SEO、DNS 配置以及你需要编写多少中间件:

  • 子路径路由将语言环境信息放入 URL 路径中,例如/fr/products/de/products。这是面向公众的网站的默认设置。搜索引擎会将每个语言环境作为独立页面进行索引,这正是多语言 SEO 所需要的。
  • 域名路由将不同地区映射到独立的域名或子域名,例如example.frde.example.com。它通过国家代码顶级域名(ccTLD)传递更强烈的地理定位信号,但会增加 DNS 配置工作,并使本地开发变得更加复杂。
  • 无路由 将所有语言版本统一在同一个 URL 下,并通过 Cookie 或存储在用户配置文件中的用户偏好来确定语言版本。搜索引擎只会看到一个 URL,因此无法单独对不同语言版本进行索引。对于无需考虑 SEO 的经过身份验证的 SaaS 应用和内部工具而言,这是一种合理的做法。 next-intl 将其作为首选方案予以支持。

对于SEO至关重要的公共网站而言,子路径路由是正确的默认选择。只有当明确需要强大的地理定位信号时,域名路由的复杂性才值得采用。

如果您不想手动管理 URL 结构,Weglot 的反向代理会自动处理子目录和子域名的路由,包括 hreflang 标签的插入以及翻译后的 URL。

next-intl 与 react-i18next 与 Lingui

正如我们之前所说,官方文档中列出了各种库,但并未推荐任何特定库。以下是主要选项的对比:

功能 next-intl react-i18next Lingui
RSC支持 原生 通过异步初始化 通过宏
SEO/hreflang 通过元数据 API 进行手动操作 通过元数据 API 进行手动操作 通过元数据 API 进行手动操作
路由 集成 通过 next-i18n-router 手册
TypeScript 严格类型化 通过插件 通过提取
格式化 通过 useFormatter 封装 Intl API FormatJS 代表 手册

‍“

next-intl是 App Router 的事实标准。它内置了 RSC 支持,集成了路由功能,且无需额外配置即可使用 TypeScript 自动补全功能。

如果您正在使用 Pages Router,或者在技术栈的其他地方已经使用了 i18next,那么react-i18next会是更合适的选择。App Router 虽然也能支持,但需要更多手动配置。

Lingui适用于已拥有.po文件处理流程的团队。翻译内容会在构建时被提取,因此未使用的消息绝不会被发布。

💡Paraglide JS虽未跻身主流之列,但作为一款基于编译器的替代方案,其生成的包体积更小,值得关注。不过,如果你对此感兴趣,就必须接受这样一个事实:它的生态系统比其他方案更为年轻。

在日期和数字格式化方面,next-intl 通过 useFormatter 封装了原生的 Intl API。其余组件则委托给FormatJS,或者由您直接调用 Intl.DateTimeFormat 和 Intl.NumberFormat。

💡 这些库负责渲染工作。但仍需有人负责生成翻译内容,并在内容变更时保持其同步。Weglot 则完全Weglot 在不同的层面上,通过代码库外部的反向代理来管理翻译内容。请不要将其视为对这些库的替代方案。

配置中间件、区域设置检测和服务器组件翻译

Next.js 16 将middleware.ts重命名为proxy.ts之所以这样做,是因为“middleware”一词容易引起误解,暗示其为通用应用内逻辑。实际上,该文件在网络边界运行,像代理一样在请求到达应用程序之前进行拦截和修改。

以下是一个完整的区域设置检测和重定向实现,它填补了官方文档中前述的空白:

// proxy.ts
import { match } from '@formatjs/intl-localematcher'
import Negotiator from 'negotiator'
import { NextRequest, NextResponse } from 'next/server'

const locales = ['en', 'fr', 'de']
const defaultLocale = 'en'

function getLocale(request: NextRequest): string {
  const cookie = request.cookies.get('NEXT_LOCALE')?.value
  if (cookie && locales.includes(cookie)) return cookie

  const headers = { 'accept-language': request.headers.get('accept-language') ?? '' }
  const languages = new Negotiator({ headers }).languages()
  return match(languages, locales, defaultLocale)
}

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl
  const hasLocale = locales.some(l => pathname.startsWith(`/${l}/`) || pathname === `/${l}`)
  if (hasLocale) return

  const locale = getLocale(request)
  request.nextUrl.pathname = `/${locale}${pathname}`
  return NextResponse.redirect(request.nextUrl)
}

‍“

文件嵌套在 app/[语言环境]/,带有 词典/ 文件夹,该文件夹位于您的布局和页面旁边。根布局将接收 区域设置 作为参数,并将其设置在 html 标签:

export default async function RootLayout({ children, params }) {
  const { locale } = await params

  return (
    <html lang={locale}>
      <body>{children}</body>
    </html>
  )
}

‍“

关于翻译,官方文档展示了一种无需依赖库的原始字典模式:

const dictionaries = {
  en: () => import('./dictionaries/en.json').then(m => m.default),
  fr: () => import('./dictionaries/fr.json').then(m => m.default),
}

export const getDictionary = async (locale: string) => dictionaries[locale]()

‍“

这虽然可行,但不支持复数形式和插值。 next-intl 同时涵盖了这两者 getTranslations() 以及 useTranslations() 服务器组件和客户端组件 分别。

无论哪种情况,翻译文件都在服务器端进行处理,只有生成的 HTML 才会发送到浏览器,因此消息文件的大小不会影响客户端包的大小。

对于客户端组件,请尽可能从服务器组件父组件中将翻译后的字符串作为 props 传递。使用 NextIntlClientProvider 仅当客户端组件需要直接处理翻译时,才使用带作用域的消息子集。

对于语言切换器,服务器组件负责渲染区域设置标签,而客户端组件则负责处理 useRouter() 调用:

// LocaleSwitcher.tsx (Server Component)
import LocaleSwitcherClient from './LocaleSwitcherClient'

export default function LocaleSwitcher({ locale }) {
  return <LocaleSwitcherClient locale={locale} labels={{ en: 'English', fr: 'Français' }} />
}

客户端会监听选项的变化,并推送新的区域设置路由:

// LocaleSwitcherClient.tsx
'use client'
import { useRouter } from 'next/navigation'

export default function LocaleSwitcherClient({ locale, labels }) {
  const router = useRouter()
  return (
    <select value={locale} onChange={e => router.push(`/${e.target.value}`)}>
      {Object.entries(labels).map(([value, label]) => (
        <option key={value} value={value}>{label}</option>
      ))}
    </select>
  )
}

如果您使用Weglot以上内容均不适用。Weglot 内置 Weglot 语言切换小工具,无需进行代理配置或使用中间件。

利用 Hreflang、网站地图和元数据进行多语言 SEO

这两款路由器均无法自动处理多语言SEO:

  • 页面路由器设置 html lang 虽然实现正确,但其文档中提到 hreflang 的实现“由您自行决定”。
  • App Router 完全没有提及 hreflang。

借助 App Router,元数据 API 能让您最接近自动化。在 生成元数据 通过 alternates.languages 对象和 Next.js 的自动注入 link rel="alternate" hreflang="..." 标签:

export async function generateMetadata({ params: { locale } }) {
  return {
    alternates: {
      languages: {
        'en': 'https://example.com/en',
        'fr': 'https://example.com/fr',
        'de': 'https://example.com/de',
      },
    },
  }
}

手动操作的部分在于为每页的每个语言版本收集正确的URL。该逻辑需要由您自行编写。

无论使用哪种路由器,还有两件事需要手动设置。必须根据不同语言环境设置规范 URL,以避免出现重复内容的信号。您的 sitemap.ts 需求 xhtml:link 每个页面的每个语言版本的条目。

hreflang 值本身,使用 hreflang="en-US" 在针对特定地区时,以及 hreflang="en" 当目标受众是所有英语使用者时。混淆这两者会向搜索引擎发送矛盾的信号。

next-intl 增加了一项部分自动化功能:其代理会自动注入 hreflang 属性 链接 在使用本地化路径名路由时,响应头会自动生成。其他路由配置仍需手动实现。

随着网站的不断发展,这将带来相当大的持续维护工作量。幸运的是Weglot 的反向代理会在服务器端自动处理所有事宜,因此翻译后的页面无需额外代码即可被搜索引擎完全收录。

Weglot多语言 SEO 策略

事实上Weglot反向代理会自动处理上一节中提到的所有内容:hreflang 标签、翻译后的 URL、规范标签以及网站地图的生成。翻译后的页面是在服务器端生成的,因此搜索引擎可以完全对其进行索引。

⚠️Weglot JavaScriptsnippet 无法带来 SEO 效益。客户端翻译无法被搜索引擎抓取。如果 SEO 很重要,则必须配置反向代理

另一个好处是构建时间。Weglot 通过其自有 CDNWeglot 翻译后的页面,而非在构建时生成这些页面。为一个Weglot网站添加 10 种语言,不会影响 下一个构建 需要。

为您的 Next.js 项目选择合适的方法

正确的选择取决于您的优化目标。

如果您正在基于 App Router 进行开发且是从头开始, next-intl 是首选方案。它在一个包中集成了路由、翻译、TypeScript 自动补全以及 RSC 支持。

如果您正在使用 Pages Router,或者您的技术栈中已经包含 i18next, react-i18next 这是阻力最小的路径。

如果多语言搜索引擎优化(SEO)和持续的翻译管理是您的首要任务,那么这两款工具都无法完全解决问题。您仍然需要实施 hreflang 标签、网站地图,并建立一套工作流程,以确保随着内容的更新,翻译内容也能及时更新。Weglot反向代理会自动处理所有这些工作。

Weglot 组件级库并不相互排斥。Weglot 在 HTML 和代理层Weglot ,因此它可以与您的组件所使用的任何库共存。

如果这种方法适合您的情况,不妨先浏览一下 Weglot JavaScript 集成页面,并尝试我们的14 天免费试用

方向图标
探索 Weglot

好事多磨。但国际交通可不会等你。

我们将让您的首选语言实时上线。您决定要走多远。立即Weglot 试用Weglot 。

这篇文章里,我们会聊聊:
火箭图标

准备好开始了吗?

要Weglot 强大功能Weglot 最好的方式Weglot 亲自体验。立即免费试用,无需任何承诺。

若您尚未准备好连接自己的网站,控制面板中已提供演示网站。

你可能也会喜欢这些文章

常见问题图标

常见问题

没有找到任何内容。

蓝色箭头

蓝色箭头

蓝色箭头