如何在 TanStack Start v1 中跨会话记住语言选择
存储用户明确选择的语言
问题
当用户明确选择了一种语言时,这代表了他们的主动偏好,这一选择应当在当前浏览器会话之外持续生效。如果没有持久化,应用会在每次访问时遗忘该偏好,迫使用户反复重新选择语言。这会增加操作阻力,给用户传递出应用不尊重其选择的信号,降低整体体验,甚至可能导致用户在完成目标操作前就离开网站。
解决方案
当用户明确选择语言时,将其选择存储在持久化 cookie 中。后续访问时,优先检查该存储的偏好,再考虑如浏览器头部等自动检测方式。如果检测到有效的已存储语言,则应将用户从站点根路径重定向到该语言路径,确保其直接进入首选语言环境,无需额外操作。
步骤
1. 创建服务端函数用于存储语言偏好
服务端函数允许你定义仅在服务端运行的逻辑,并可在应用任意位置调用。请定义一个函数,将所选语言写入 cookie。
import { createServerFn } from "@tanstack/react-start";
import { setCookie } from "@tanstack/react-start/server";
const LOCALE_COOKIE = "user_locale";
const COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
export const saveLocalePreference = createServerFn({ method: "POST" })
.validator((locale: string) => locale)
.handler(async ({ data }) => {
setCookie(LOCALE_COOKIE, data, {
maxAge: COOKIE_MAX_AGE,
path: "/",
sameSite: "lax",
});
return { success: true };
});
该服务端函数使用 setCookie(来自 @tanstack/react-start/server)来存储语言偏好,使其在后续请求中可用。
2. 用户选择语言后调用保存函数
在你的语言切换组件中,用户做出选择后调用该服务端函数。
import { useNavigate } from "@tanstack/react-router";
import { saveLocalePreference } from "./locale-preference";
export function LanguageSwitcher({ currentLocale }: { currentLocale: string }) {
const navigate = useNavigate();
const handleLocaleChange = async (newLocale: string) => {
await saveLocalePreference({ data: newLocale });
navigate({ to: `/${newLocale}` });
};
return (
<select
value={currentLocale}
onChange={(e) => handleLocaleChange(e.target.value)}
>
<option value="en">English</option>
<option value="es">Español</option>
<option value="fr">Français</option>
</select>
);
}
这样可以确保在跳转到新语言环境前,用户偏好已被保存。
3. 创建用于读取已存储偏好的服务器函数
定义一个函数,从 cookie 中获取已存储的语言区域。
import { createServerFn } from "@tanstack/react-start";
import { getCookie } from "@tanstack/react-start/server";
const LOCALE_COOKIE = "user_locale";
export const getStoredLocale = createServerFn({ method: "GET" }).handler(
async () => {
const stored = getCookie(LOCALE_COOKIE);
return stored || null;
},
);
来自 @tanstack/react-start/server 的 getCookie 函数会读取之前访问时设置的 cookie 值。
4. 在根路由检查已存储的偏好
在路由的 beforeLoad 回调中使用 redirect 函数,在检测到已存储偏好时触发重定向。
import { createFileRoute, redirect } from "@tanstack/react-router";
import { getStoredLocale } from "./locale-preference";
const SUPPORTED_LOCALES = ["en", "es", "fr"];
const DEFAULT_LOCALE = "en";
export const Route = createFileRoute("/")({
beforeLoad: async () => {
const stored = await getStoredLocale();
if (stored && SUPPORTED_LOCALES.includes(stored)) {
throw redirect({ to: `/${stored}` });
}
throw redirect({ to: `/${DEFAULT_LOCALE}` });
},
});
这会优先检查已存储的偏好,如果有效则重定向到对应语言区域,否则回退到默认设置。
5. 校验已存储的语言区域是否受支持
添加校验,确保已存储的值是受支持的语言区域后再进行重定向。
import { createFileRoute, redirect } from "@tanstack/react-router";
import { getStoredLocale } from "./locale-preference";
const SUPPORTED_LOCALES = ["en", "es", "fr", "de", "ja"] as const;
function isValidLocale(value: string | null): value is string {
return value !== null && SUPPORTED_LOCALES.includes(value as any);
}
export const Route = createFileRoute("/")({
beforeLoad: async () => {
const stored = await getStoredLocale();
const locale = isValidLocale(stored) ? stored : "en";
throw redirect({ to: `/${locale}` });
},
});
这样可以防止因 cookie 被篡改或值过期而重定向到无效或不受支持的语言区域。