Как запомнить выбор языка между сессиями в 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;
  },
);

Функция getCookie из @tanstack/react-start/server читает значение cookie, установленное при предыдущих посещениях.

4. Проверьте сохранённое предпочтение на корневом маршруте

Используйте функцию redirect в callback маршрута beforeLoad, чтобы выполнить редирект, если найдено сохранённое предпочтение.

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 или устаревших значений.