Как реализовать маршрутизацию на основе локали в TanStack Start v1

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

Проблема

При создании многоязычного приложения один фундаментальный выбор определяет всё остальное: как приложение будет определять, на каком языке отображать контент? Без явного механизма URL /about становится неоднозначным — он может представлять контент на любом языке. Пользователи не могут делиться ссылками на конкретные языковые версии, а поисковые системы испытывают трудности с определением, какая версия предназначена для какой аудитории. Эта неоднозначность создает проблемы как для пользовательского опыта, так и для SEO, так как нет четкого способа различать языковые варианты одного и того же контента.

Решение

Добавьте идентификатор языка прямо в путь URL, например, /en/about или /fr/about. Это делает каждый путь уникальным для конкретного языка, устраняя неоднозначность как для пользователей, так и для поисковых систем. Используя параметр локали в структуре маршрута, каждый URL становится самодокументируемым и удобным для обмена, позволяя пользователям сохранять закладки или делиться ссылками на контент на предпочитаемом языке, а также обеспечивая правильную индексацию каждого языкового варианта поисковыми системами.

Шаги

1. Создайте маршрут с макетом для локали

Определите необязательный параметр локали, используя синтаксис {-$locale}, чтобы создать гибкие шаблоны маршрутов, где параметр локали является необязательным. TanStack Start использует маршрутизацию на основе файлов, где маршруты определяются в каталоге src/routes.

import { createFileRoute, Outlet } from "@tanstack/react-router";

export const Route = createFileRoute("/{-$locale}")({
  component: LocaleLayout,
});

function LocaleLayout() {
  return <Outlet />;
}

Этот маршрут соответствует как /about (локаль не определена), так и /en/about (локаль — "en"), что позволяет поддерживать URL-адреса с префиксом локали или без него.

2. Создайте дочерние маршруты под макетом для локали

TanStack Router использует вложенные маршруты для сопоставления URL с правильным деревом компонентов для отображения. Создавайте файлы маршрутов как дочерние элементы параметра локали, чтобы наследовать локаль из URL.

import { createFileRoute } from "@tanstack/react-router";

export const Route = createFileRoute("/{-$locale}/about")({
  component: AboutPage,
});

function AboutPage() {
  const { locale } = Route.useParams();
  const currentLocale = locale || "en";

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

Структура файла маршрута {-$locale}/about.tsx создает пути, такие как /about и /en/about, которые оба отображают один и тот же компонент с доступом к параметру локали.

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

Используйте хук useParams, чтобы считать локаль из URL и определить, какой язык отображать.

import { createFileRoute } from "@tanstack/react-router";

export const Route = createFileRoute("/{-$locale}/products")({
  component: ProductsPage,
});

function ProductsPage() {
  const { locale } = Route.useParams();
  const displayLocale = locale || "en";

  return (
    <div>
      <h1>{displayLocale === "fr" ? "Produits" : "Продукты"}</h1>
    </div>
  );
}

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

4. Создание ссылок, сохраняющих локаль

Используйте компонент Link с параметром локали для навигации между страницами, сохраняя текущий язык.

import { Link, createFileRoute } from "@tanstack/react-router";

export const Route = createFileRoute("/{-$locale}/")({
  component: HomePage,
});

function HomePage() {
  const { locale } = Route.useParams();

  return (
    <nav>
      <Link to="/{-$locale}/about" params={{ locale }}>
        О нас
      </Link>
      <Link to="/{-$locale}/products" params={{ locale }}>
        Продукты
      </Link>
    </nav>
  );
}

Передавая текущую локаль в свойстве params, ссылки автоматически генерируют URL, соответствующие текущему языковому контексту пользователя, например, /fr/about при просмотре французской версии.