如何在 React Router v7 中实现基于语言环境的路由

使用语言环境段配置路由

问题

在构建多语言应用时,一个最根本的决策会影响后续所有实现:应用如何确定显示哪种语言?如果没有明确的机制,URL /about 就会变得模糊——它可能代表任何语言的内容。用户无法分享特定语言版本的链接,搜索引擎也难以判断哪个版本对应哪个受众。这种不明确会影响用户体验和内容的可发现性。

解决方案

直接在 URL 路径中加入语言标识符,例如 /en/about/fr/about。这样每个路径都唯一对应某种语言,消除了用户和搜索引擎的歧义。通过将路由定义为以语言环境参数为首段,应用可以从 URL 中提取 locale,并据此决定显示哪种语言的内容。这种方式确保每个 URL 都能明确标识页面及其语言。

步骤

1. 使用语言环境参数定义路由

app/routes.ts 中配置路由时,将 locale 参数作为每个路径的首段。

import { type RouteConfig, route, index } from "@react-router/dev/routes";

export default [
  route(":locale", "./localized-layout.tsx", [
    index("./home.tsx"),
    route("about", "./about.tsx"),
    route("contact", "./contact.tsx"),
  ]),
] satisfies RouteConfig;

冒号前缀使 locale 成为动态段,会从 URL 解析并作为参数传递给路由组件。这样会生成如 /en/en/about/fr/contact 这样的路由,其中首段始终为 locale。

2. 创建布局组件以提取 locale

子路由通过父路由中的 Outlet 组件进行渲染。请创建一个布局,用于提取 locale 参数并渲染嵌套路由。

import { Outlet, useParams } from "react-router";

export default function LocalizedLayout() {
  const { locale } = useParams();

  return (
    <div>
      <nav>
        <a href={`/${locale}`}>Home</a>
        <a href={`/${locale}/about`}>About</a>
        <a href={`/${locale}/contact`}>Contact</a>
      </nav>
      <Outlet />
    </div>
  );
}

useParams 钩子用于从 URL 中获取动态片段的值。该布局利用 locale 构建导航链接,并通过 Outlet 将渲染控制权交给子路由。

3. 在页面组件中访问 locale

在任意路由组件中使用 useParams 钩子访问 locale 参数。

import { useParams } from "react-router";

export default function About() {
  const { locale } = useParams();

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

在本地化布局中渲染的每个组件都可以从 URL 中提取 locale。该值可用于加载相应的翻译、格式化日期和数字,或进行其他与 locale 相关的处理。

将锚点标签替换为 Link 组件,以实现基于客户端路由的导航。

import { Outlet, useParams, Link } from "react-router";

export default function LocalizedLayout() {
  const { locale } = useParams();

  return (
    <div>
      <nav>
        <Link to={`/${locale}`}>Home</Link>
        <Link to={`/${locale}/about`}>About</Link>
        <Link to={`/${locale}/contact`}>Contact</Link>
      </nav>
      <Outlet />
    </div>
  );
}

Link 组件在客户端处理路由,防止网页重新加载,实现页面间的平滑跳转。每个链接都包含 locale 参数,以在导航过程中保持语言环境。