TanStack Start v1でセッション間で言語選択を記憶する方法
ユーザーの明示的な言語選択を保存する
問題
ユーザーが明示的に言語を選択した場合、その選択は現在のブラウザセッションを超えて持続すべき意図的な設定です。この設定が保持されないと、アプリケーションは訪問するたびにこの設定を忘れてしまい、ユーザーは毎回言語を再選択する必要があります。これにより摩擦が生じ、アプリケーションがユーザーの選択を尊重していないという印象を与え、全体的なユーザー体験が低下し、ユーザーが目的のタスクを完了する前にサイトを離れてしまう可能性があります。
解決策
ユーザーが明示的に選択した言語設定を永続的なクッキーに保存します。その後の訪問時には、ブラウザヘッダーなどの自動検出方法に頼る前に、この保存された設定を確認します。有効な保存言語が見つかった場合、ユーザーをサイトのルートからその言語のパスにリダイレクトし、追加の手順なしで直接希望のロケールにアクセスできるようにします。
手順
1. 言語設定を保存するサーバー関数を作成する
サーバー関数を使用すると、アプリケーションのどこからでも呼び出せるサーバー専用のロジックを定義できます。選択されたロケールをクッキーに書き込む関数を定義します。
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 };
});
このサーバー関数は、@tanstack/react-start/serverのsetCookieを使用してロケール設定を保存し、将来のリクエストで利用できるようにします。
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. 保存された設定を読み取るサーバー関数を作成する
クッキーから保存されたロケールを取得する関数を定義します。
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関数は、以前の訪問時に設定されたクッキー値を読み取ります。
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}` });
},
});
これにより、無効または非サポートのロケールへのリダイレクトを防ぎ、クッキーの改ざんや古い値から保護します。