Как валидировать параметры локали в URL в TanStack Start v1
Корректная обработка неподдерживаемых кодов локалей
Проблема
Когда языковые коды становятся частью структуры URL, они превращаются во ввод пользователя, который нужно валидировать. Пользователь может вручную ввести любую строку в сегмент локали — /xx/about, /gibberish/contact или /typo123/products — так же легко, как и валидные коды вроде /en/about или /fr/contact. Без валидации приложение может попытаться загрузить переводы для несуществующих локалей, показать некорректный контент или даже упасть. Каждая невалидная локаль — это потенциальный тупик, из которого пользователь не сможет восстановиться или перейти на рабочую страницу.
Невалидированные параметры локали приводят к непредсказуемому поведению. Приложение может тихо не загрузить переводы, отобразить смесь запасного и отсутствующего контента или выбросить ошибку во время выполнения при обращении к ключам перевода. Пользователи, которые переходят по битым ссылкам или ошибаются в URL, остаются без понятной обратной связи о том, что пошло не так и как это исправить.
Решение
Валидируйте параметр локали из URL, сверяя его со списком поддерживаемых локалей в функции beforeLoad маршрута. Если локаль невалидна или отсутствует, перенаправьте пользователя на корректный URL с локалью по умолчанию или выбросьте ошибку not-found, чтобы показать информативную страницу ошибки. Это гарантирует, что обрабатываются только поддерживаемые локали, и пользователь всегда попадает на рабочую, переводимую страницу.
Функция beforeLoad выполняется до загрузки маршрута, поэтому это идеальное место для проверки локали. Если выбросить ошибку redirect() или notFound(), маршрут не будет отрисован с невалидной локалью, а пользователь сразу попадёт в рабочее состояние.
Шаги
1. Определите поддерживаемые локали
Создайте константный массив с валидными кодами локалей и функцию для их типобезопасной проверки.
const SUPPORTED_LOCALES = ["en", "fr", "es", "de"] as const;
type Locale = (typeof SUPPORTED_LOCALES)[number];
function isValidLocale(locale: string | undefined): locale is Locale {
return SUPPORTED_LOCALES.includes(locale as Locale);
}
Это даст единый источник правды по поддерживаемым локалям и переиспользуемую функцию валидации, которую понимает TypeScript.
2. Создайте маршрут layout с валидацией локали
Используйте необязательный параметр локали и валидируйте его в beforeLoad.
import { createFileRoute, redirect } from "@tanstack/react-router";
const DEFAULT_LOCALE: Locale = "en";
export const Route = createFileRoute("/{-$locale}")({
beforeLoad: ({ params }) => {
const { locale } = params;
if (locale && !isValidLocale(locale)) {
throw redirect({
to: "/{-$locale}",
params: { locale: undefined },
replace: true,
});
}
return {
locale: (locale as Locale) || DEFAULT_LOCALE,
};
},
});
Функция beforeLoad проверяет параметр локали. Если он есть, но невалидный, пользователя перенаправляют на тот же путь без префикса локали — это приведёт к дефолтной локали. Проверенная локаль возвращается в контексте для использования во вложенных маршрутах.
3. Валидируйте локаль во вложенных маршрутах
Для маршрутов, где локаль обязательна, проверьте её и выбросьте notFound(), если она невалидна.
import { createFileRoute, notFound } from "@tanstack/react-router";
export const Route = createFileRoute("/{-$locale}/products")({
beforeLoad: ({ params }) => {
const { locale } = params;
if (locale && !isValidLocale(locale)) {
throw notFound();
}
return {
locale: (locale as Locale) || DEFAULT_LOCALE,
};
},
component: ProductsPage,
});
function ProductsPage() {
const { locale } = Route.useRouteContext();
return <div>Products in {locale}</div>;
}
Этот подход показывает страницу «не найдено» для невалидных локалей вместо редиректа — это удобно, если вы хотите явно показать, что URL некорректен, а не исправлять его молча.
4. Добавьте компонент not-found для невалидных локалей
Определите notFoundComponent на корневом маршруте, чтобы корректно обрабатывать ошибки невалидных локалей.
import { createRootRoute } from "@tanstack/react-router";
export const Route = createRootRoute({
notFoundComponent: () => {
return (
<div>
<h1>Page Not Found</h1>
<p>The language or page you requested does not exist.</p>
<a href="/">Go to home page</a>
</div>
);
},
});
Этот компонент отображается, когда notFound() выбрасывается из beforeLoad, чтобы пользователь видел понятное сообщение и мог вернуться на рабочую страницу.