如何在 TanStack Start v1 中验证 URL 中的 locale 参数
优雅地处理不受支持的语言代码
问题
当语言代码成为 URL 结构的一部分时,它们就变成了需要验证的用户输入。用户可以在 locale 段中手动输入任意字符串——如 /xx/about、/gibberish/contact 或 /typo123/products——就像输入有效代码 /en/about 或 /fr/contact 一样简单。如果不进行验证,应用可能会尝试加载不存在的语言环境的翻译,导致内容异常或崩溃。每一个无效的 locale 都可能成为用户无法恢复或跳转到正常页面的死胡同。
未验证的 locale 参数会导致不可预测的行为。应用可能会悄无声息地加载失败,渲染出混合的兜底内容和缺失内容,或者在访问翻译 key 时抛出运行时错误。用户如果点击了损坏的链接或输错了 URL,将无法获得明确的反馈,不知道哪里出错,也不知道如何修复。
解决方案
在路由的 beforeLoad 函数中,将 URL 中的 locale 参数与支持的语言环境列表进行校验。当 locale 无效或缺失时,将用户重定向到带有默认 locale 的有效 URL,或抛出 not-found 错误以显示友好的错误页面。这样可以确保只处理受支持的 locale,并让用户始终落在可翻译的有效页面上。
beforeLoad 函数会在路由加载前运行,是检查 locale 的理想位置。通过抛出 redirect() 或 notFound() 错误,可以防止路由在无效 locale 下渲染,并引导用户回到正常状态。
步骤
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. 创建带有语言区域校验的布局路由
使用可选的语言区域参数,并在 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 函数会检查 locale 参数。如果存在但无效,则用户会被重定向到没有 locale 前缀的相同路径(即默认语言区域)。校验通过的 locale 会在 context 中返回,供子路由使用。
3. 在嵌套路由中校验语言区域
对于需要 locale 的路由,进行校验,如果无效则抛出 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>;
}
这种方式会在 locale 无效时显示 not-found 页面,而不是重定向。当你希望明确告知 URL 格式错误而不是自动修正时,这种方式非常有用。
4. 为无效语言区域添加 not-found 组件
在根路由上定义 notFoundComponent,以优雅地处理无效 locale 错误。
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 抛出时,该组件会渲染,为用户提供清晰的提示信息和返回有效页面的方式。