如何在 Next.js(Pages Router)v16 中实现基于语言环境的路由

使用语言环境片段配置路由

问题

在构建多语言应用时,有一个核心决策会影响后续所有实现:应用如何确定要显示哪种语言?如果没有明确的机制,URL /about 就会变得模糊——它可能对应任何语言的内容。用户无法分享特定语言版本的链接,搜索引擎也难以判断每个版本对应的受众。这种不明确会影响用户体验和 SEO,因为无法清晰地标识或收藏某种语言的内容。

解决方案

通过配置 Next.js 内置的 i18n 路由支持,将语言标识符直接放入 URL 路径。在 Next.js 配置中声明要支持的语言环境(locale)和默认语言环境。Next.js 会自动处理路由,使像 /fr/about/nl-NL/about 这样的路径可用,而默认语言环境则没有前缀。这样每个路径都唯一对应某种语言,消除了用户和搜索引擎的歧义。

步骤

1. 在 next.config.js 中添加 i18n 配置

在 next.config.js 文件中添加 i18n 配置,声明应用支持的语言环境。

module.exports = {
  i18n: {
    locales: ["en-US", "fr", "nl-NL"],
    defaultLocale: "en-US",
  },
};

Locale 是 UTS 语言环境标识符,一种标准化格式,通常由语言、地区和脚本组成,并用连字符分隔。defaultLocale 用于访问没有语言前缀的路径时。

2. 在页面中获取语言环境信息

在页面组件中使用 useRouter() hook 获取语言环境信息。

import { useRouter } from "next/router";

export default function AboutPage() {
  const router = useRouter();
  const { locale, locales, defaultLocale } = router;

  return (
    <div>
      <h1>About Us</h1>
      <p>Current locale: {locale}</p>
    </div>
  );
}

locale 属性包含当前激活的 locale,locales 包含所有已配置的 locale,defaultLocale 包含已配置的默认 locale。

3. 在数据获取函数中访问 locale

在使用 getStaticProps 或 getServerSideProps 进行页面预渲染时,locale 信息会通过 context 提供。

import { GetStaticProps } from "next";

export const getStaticProps: GetStaticProps = async (context) => {
  const { locale } = context;

  const messages = await import(`../messages/${locale}.json`);

  return {
    props: {
      messages: messages.default,
    },
  };
};

这样可以根据激活的 locale 在构建时或请求时加载特定于 locale 的数据。

4. locale 之间的链接

使用 next/link 并传递 locale 属性,可以跳转到其他 locale。

import Link from "next/link";

export default function LanguageSwitcher() {
  return (
    <nav>
      <Link href="/about" locale="en-US">
        English
      </Link>
      <Link href="/about" locale="fr">
        Français
      </Link>
      <Link href="/about" locale="nl-NL">
        Nederlands
      </Link>
    </nav>
  );
}

如果未提供 locale 属性,则在客户端跳转时会使用当前激活的 locale。locale 属性允许用户在保持同一逻辑页面的同时切换语言。

5. 为所有 locale 生成静态路径

在使用 getStaticPaths 时,已配置的 locales 会通过 context 参数下的 locales 提供,已配置的 defaultLocale 通过 defaultLocale 提供。

import { GetStaticPaths } from "next";

export const getStaticPaths: GetStaticPaths = async (context) => {
  const { locales } = context;

  const paths = locales.flatMap((locale) => [
    { params: { slug: "getting-started" }, locale },
    { params: { slug: "advanced" }, locale },
  ]);

  return {
    paths,
    fallback: false,
  };
};

这样可以确保所有 locale 版本的动态页面都在构建时预渲染。