Как запомнить выбор языка между сессиями в 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 в обратном вызове 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 или устаревших значений.