Как сохранять локаль в навигационных ссылках в React Router v7
Сохраняйте локаль при внутренней навигации
Проблема
Когда информация о локали закодирована в пути URL, каждая навигационная ссылка должна сохранять эту локаль, чтобы обеспечить пользователю единообразный опыт. Если пользователь просматривает французскую версию вашего сайта и кликает по ссылке на /about, он ожидает остаться на французском и перейти на /fr/about. Если ссылки не учитывают локаль, пользователь внезапно попадает на сайт на языке по умолчанию, теряя контекст и вынужден вручную возвращать нужный язык. Это создает неудобства и портит локализованный опыт.
Жестко прописывать префикс локали в каждой ссылке — это рискованно и делает кодовую базу хрупкой. По мере навигации пользователя по приложению активная локаль может меняться, и вручную обновлять сотни ссылок становится невозможно.
Решение
Создайте собственный компонент Link, который автоматически считывает текущую локаль из URL и добавляет её ко всем внутренним путям навигации. Обернув компонент Link из React Router, вы централизуете обработку локали в одном месте. Обёртка извлекает параметр локали из текущего маршрута и гарантирует, что каждый путь назначения его содержит, чтобы навигация всегда сохраняла выбранный пользователем язык без ручных действий.
Такой подход позволяет держать определения ссылок чистыми и не зависящими от локали по всему приложению, при этом гарантируя, что контекст локали всегда передаётся при каждом клике.
Шаги
1. Создайте компонент-обёртку Link с поддержкой локали
Создайте собственный компонент, который использует useParams для извлечения текущей локали из URL и оборачивает компонент Link из React Router, чтобы добавлять локаль к целевому пути.
import { Link, useParams } from "react-router";
import type { LinkProps } from "react-router";
export function LocaleLink({ to, ...props }: LinkProps) {
const { locale } = useParams<{ locale: string }>();
const localizedTo =
typeof to === "string"
? `/${locale}${to.startsWith("/") ? to : `/${to}`}`
: {
...to,
pathname: `/${locale}${to.pathname?.startsWith("/") ? to.pathname : `/${to.pathname}`}`,
};
return <Link to={localizedTo} {...props} />;
}
Этот компонент считывает параметр locale из текущего маршрута и автоматически добавляет его в начало любого пути, который вы передаёте в prop to, поддерживая как строковые, так и объектные формы.
2. Используйте компонент LocaleLink по всему приложению
Заменяйте стандартные компоненты Link на LocaleLink везде, где нужно сохранять локаль при навигации.
import { LocaleLink } from "./LocaleLink";
export function Navigation() {
return (
<nav>
<LocaleLink to="/">Home</LocaleLink>
<LocaleLink to="/about">About</LocaleLink>
<LocaleLink to="/products">Products</LocaleLink>
</nav>
);
}
Когда пользователь на /fr/products кликает по ссылке About, он переходит на /fr/about. Префикс локали добавляется автоматически, не усложняя определение ссылки.
3. Обрабатывайте крайние случаи для абсолютных путей и внешних ссылок
Расширьте обёртку, чтобы определять, когда путь уже содержит локаль или ведёт на внешний URL, чтобы избежать двойного префикса или ошибок при переходе на внешние ресурсы.
import { Link, useParams } from "react-router";
import type { LinkProps } from "react-router";
export function LocaleLink({ to, ...props }: LinkProps) {
const { locale } = useParams<{ locale: string }>();
if (!locale) {
return <Link to={to} {...props} />;
}
const isExternal =
typeof to === "string" &&
(to.startsWith("http://") || to.startsWith("https://"));
const alreadyLocalized =
typeof to === "string" && to.startsWith(`/${locale}/`);
if (isExternal || alreadyLocalized) {
return <Link to={to} {...props} />;
}
const localizedTo =
typeof to === "string"
? `/${locale}${to.startsWith("/") ? to : `/${to}`}`
: {
...to,
pathname: `/${locale}${to.pathname?.startsWith("/") ? to.pathname : `/${to.pathname}`}`,
};
return <Link to={localizedTo} {...props} />;
}
Это защищает от двойного префикса, если путь уже начинается с локали, и пропускает внешние URL без изменений, чтобы компонент работал стабильно во всех сценариях навигации.