Как запомнить выбор языка между сессиями в React Router v7
Сохраняйте явный выбор языка пользователя
Проблема
Когда пользователь явно выбирает язык, это отражает его предпочтения и должно иметь приоритет над автоматическим определением. Если не сохранять этот выбор, он исчезает после закрытия браузера или окончания сессии. При следующем посещении приложение запускается заново, и пользователю снова приходится выбирать язык. Такое повторение показывает, что приложение не уважает предпочтения пользователя, вызывает раздражение и снижает доверие.
Решение
Сохраняйте выбранный пользователем язык в постоянном хранилище, например, в cookie, когда он делает выбор. При последующих посещениях сначала проверяйте наличие сохранённого предпочтения, прежде чем использовать заголовки браузера или другие методы определения. Если найден корректный сохранённый язык, автоматически перенаправляйте пользователя на соответствующий маршрут. Это гарантирует, что явный выбор пользователя будет иметь приоритет и сохранится между сессиями.
Шаги
1. Создайте cookie для хранения предпочтения языка
Определите cookie, в котором будет храниться выбранный пользователем язык, с длительным сроком действия.
import { createCookie } from "react-router";
export const languagePreference = createCookie("language-preference", {
maxAge: 31536000,
httpOnly: false,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
});
Этот cookie сохраняется на год и доступен для чтения на стороне клиента.
2. Добавьте действие для сохранения выбора языка
Создайте действие, которое обрабатывает отправку формы выбора языка и сохраняет выбор в cookie.
import { redirect } from "react-router";
import type { Route } from "./+types/root";
import { languagePreference } from "./cookies";
export async function action({ request }: Route.ActionArgs) {
const formData = await request.formData();
const selectedLanguage = formData.get("language");
if (typeof selectedLanguage === "string") {
return redirect(`/${selectedLanguage}`, {
headers: {
"Set-Cookie": await languagePreference.serialize(selectedLanguage),
},
});
}
return redirect("/");
}
Когда пользователь отправляет свой выбор языка, это действие сохраняет его в cookie и перенаправляет на нужный языковой маршрут.
3. Создайте компонент выбора языка
Сделайте компонент формы, который позволит пользователю выбрать предпочитаемый язык.
import { Form } from "react-router";
export function LanguageSelector({
currentLanguage,
}: {
currentLanguage: string;
}) {
return (
<Form method="post">
<select
name="language"
defaultValue={currentLanguage}
onChange={(e) => e.currentTarget.form?.requestSubmit()}
>
<option value="en">English</option>
<option value="es">Español</option>
<option value="fr">Français</option>
<option value="de">Deutsch</option>
</select>
</Form>
);
}
Этот компонент отправляет данные автоматически, когда пользователь меняет выбор, что запускает действие по сохранению предпочтения.
4. Проверка сохранённого предпочтения в корневом загрузчике
Добавьте логику в загрузчик корневого маршрута, чтобы проверять сохранённое языковое предпочтение и перенаправлять пользователя соответствующим образом.
import { redirect } from "react-router";
import type { Route } from "./+types/root";
import { languagePreference } from "./cookies";
export async function loader({ request }: Route.LoaderArgs) {
const url = new URL(request.url);
const cookieHeader = request.headers.get("Cookie");
const storedLanguage = await languagePreference.parse(cookieHeader);
if (url.pathname === "/" && storedLanguage) {
return redirect(`/${storedLanguage}`);
}
return null;
}
Когда пользователь заходит на корневой путь, этот загрузчик проверяет наличие сохранённого языкового предпочтения и перенаправляет на выбранный языковой маршрут, если оно есть.
5. Проверьте сохранённый язык на соответствие поддерживаемым локалям
Убедитесь, что сохранённое предпочтение действительно, прежде чем использовать его для перенаправления.
import { redirect } from "react-router";
import type { Route } from "./+types/root";
import { languagePreference } from "./cookies";
const SUPPORTED_LANGUAGES = ["en", "es", "fr", "de"];
export async function loader({ request }: Route.LoaderArgs) {
const url = new URL(request.url);
const cookieHeader = request.headers.get("Cookie");
const storedLanguage = await languagePreference.parse(cookieHeader);
if (
url.pathname === "/" &&
storedLanguage &&
SUPPORTED_LANGUAGES.includes(storedLanguage)
) {
return redirect(`/${storedLanguage}`);
}
return null;
}
Эта проверка предотвращает перенаправление на невалидные или неподдерживаемые языковые маршруты, если значение cookie было изменено вручную или если список поддерживаемых языков изменился после сохранения предпочтения.