如何在 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>
);
}
该组件会读取当前路径名,确定当前激活的语言环境,并为每个支持的语言渲染一个链接,保留当前页面结构、查询参数和哈希片段。用户可以在保持同一逻辑页面的情况下切换语言。