Как реализовать маршрутизацию на основе локали в React Router v7

Настройка маршрутизации с сегментами локали

Проблема

При создании многоязычного приложения один фундаментальный вопрос определяет всё остальное: как приложение будет знать, на каком языке отображать контент? Без явного механизма 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}`}>Главная</a>
        <a href={`/${locale}/about`}>О нас</a>
        <a href={`/${locale}/contact`}>Контакты</a>
      </nav>
      <Outlet />
    </div>
  );
}

Хук useParams извлекает значение динамического сегмента из URL. Макет использует эту локаль для построения навигационных ссылок и передаёт управление отображением дочерним маршрутам через Outlet.

3. Доступ к локали в компонентах страницы

Используйте хук useParams в любом компоненте маршрута, чтобы получить доступ к параметру локали.

import { useParams } from "react-router";

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

  return (
    <div>
      <h1>О нас</h1>
      <p>Текущая локаль: {locale}</p>
    </div>
  );
}

Каждый компонент, отображаемый в локализованном макете, может извлечь локаль из URL. Это значение можно использовать для загрузки соответствующих переводов, форматирования дат и чисел или принятия любых других решений, зависящих от локали.

Замените теги <a> на компоненты 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 обрабатывает маршрутизацию на стороне клиента и предотвращает перезагрузку веб-страницы, обеспечивая плавную навигацию между страницами. Каждая ссылка включает параметр локали, чтобы сохранить языковой контекст при навигации.