如何在 React Router v7 中实现基于 locale 的路由

配置带有 locale 段的路由

问题

在构建多语言应用程序时,一个基本的决定会影响其他所有方面:应用程序如何知道显示哪种语言?如果没有明确的机制,URL /about 就会变得模糊不清——它可能代表任何语言的内容。用户无法分享特定语言版本的链接,搜索引擎也难以理解哪个版本属于哪个受众。这种模糊性会对用户体验和可发现性造成问题。

解决方案

在 URL 路径中直接加入语言标识符,例如 /en/about/fr/about。这使得每个路径都唯一对应一种特定语言,消除了用户和搜索引擎的模糊性。通过将路由定义为以语言参数作为第一个段落,应用程序可以从 URL 中提取语言参数,并据此确定显示的内容语言。这种方法确保每个 URL 都明确标识页面及其语言。

步骤

1. 定义包含语言参数的路由

app/routes.ts 中配置路由,使每个路径的第一个段落包含语言参数。

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 这样的路由,其中第一个段落始终是语言标识符。

2. 创建一个提取语言参数的布局组件

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

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 中检索动态段落的值。布局组件使用此语言参数构建导航链接,并通过 Outlet 将渲染控制权传递给子路由。

3. 在页面组件中访问 locale

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

import { useParams } from "react-router";

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

  return (
    <div>
      <h1>关于我们</h1>
      <p>当前的 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}`}>主页</Link>
        <Link to={`/${locale}/about`}>关于</Link>
        <Link to={`/${locale}/contact`}>联系</Link>
      </nav>
      <Outlet />
    </div>
  );
}

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