Как создать компонент переключения языка в TanStack Start v1

Меняйте язык, оставаясь на той же странице

Проблема

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

Решение

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

Шаги

1. Определите поддерживаемые локали

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

export const locales = ["en", "fr", "es", "de"] as const;

export type Locale = (typeof locales)[number];

export const defaultLocale: Locale = "en";

Эта конфигурация будет единственным источником правды по доступным языкам и будет использоваться для генерации ссылок переключателя.

2. Создайте хелпер для извлечения текущей локали из пути

Напишите утилиту, которая парсит путь и извлекает языковой сегмент, если он есть.

import { defaultLocale, locales, type Locale } from "./locales";

export function getLocaleFromPathname(pathname: string): Locale {
  const segments = pathname.split("/").filter(Boolean);
  const firstSegment = segments[0];

  if (firstSegment && locales.includes(firstSegment as Locale)) {
    return firstSegment as Locale;
  }

  return defaultLocale;
}

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

3. Создайте вспомогательную функцию для формирования локализованных путей

Напишите функцию, которая принимает текущий путь и целевую локаль, а затем формирует новый путь, заменяя сегмент локали.

import { defaultLocale, locales, type Locale } from "./locales";

export function getLocalizedPath(
  pathname: string,
  targetLocale: Locale,
): string {
  const segments = pathname.split("/").filter(Boolean);
  const firstSegment = segments[0];

  const hasLocalePrefix =
    firstSegment && locales.includes(firstSegment as Locale);

  if (hasLocalePrefix) {
    segments[0] = targetLocale;
  } else {
    segments.unshift(targetLocale);
  }

  return "/" + segments.join("/");
}

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

4. Создайте компонент переключения языка

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

import { Link } from "@tanstack/react-router";
import { useLocation } from "@tanstack/react-router";
import { locales, type Locale } from "./locales";
import { getLocaleFromPathname, getLocalizedPath } from "./locale-helpers";

export function LanguageSwitcher() {
  const location = useLocation();
  const currentLocale = getLocaleFromPathname(location.pathname);

  return (
    <nav>
      <ul>
        {locales.map((locale) => {
          const isActive = locale === currentLocale;
          const localizedPath = getLocalizedPath(location.pathname, locale);

          return (
            <li key={locale}>
              <Link
                to={localizedPath}
                search={location.search}
                hash={location.hash}
                aria-current={isActive ? "page" : undefined}
              >
                {locale.toUpperCase()}
              </Link>
            </li>
          );
        })}
      </ul>
    </nav>
  );
}

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