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