Cómo recordar la selección de idioma entre sesiones en React Router v7
Almacena la elección explícita de idioma del usuario
Problema
Cuando un usuario selecciona explícitamente un idioma, esa elección refleja su preferencia y debe prevalecer sobre cualquier detección automática. Sin persistencia, esta selección desaparece cuando el navegador se cierra o la sesión termina. En la siguiente visita, la aplicación comienza de cero, obligando al usuario a seleccionar su idioma nuevamente. Esta repetición indica que la aplicación no respeta sus preferencias, creando fricción y disminuyendo la confianza.
Solución
Almacena la elección de idioma del usuario en una ubicación persistente como una cookie cuando realice una selección. En visitas posteriores, verifica esta preferencia almacenada antes de recurrir a los encabezados del navegador u otros métodos de detección. Si se encuentra un idioma almacenado válido, redirige al usuario automáticamente a la ruta de ese idioma. Esto garantiza que su elección explícita tenga prioridad y persista entre sesiones.
Pasos
1. Crea una cookie para almacenar la preferencia de idioma
Define una cookie que contendrá el idioma seleccionado por el usuario con un tiempo de expiración prolongado.
import { createCookie } from "react-router";
export const languagePreference = createCookie("language-preference", {
maxAge: 31536000,
httpOnly: false,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
});
Esta cookie persiste durante un año y es accesible para el código del lado del cliente para leer la preferencia.
2. Añade una acción para almacenar la selección de idioma
Crea una acción que maneje los envíos de formularios de selección de idioma y almacene la elección en la 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("/");
}
Cuando el usuario envía su elección de idioma, esta acción la almacena en la cookie y lo redirige a la ruta de idioma apropiada.
3. Crea un componente de selección de idioma
Construye un componente de formulario que permita a los usuarios elegir su idioma preferido.
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>
);
}
Este componente se envía automáticamente cuando el usuario cambia la selección, activando la acción que almacena la preferencia.
4. Verificar la preferencia almacenada en el loader raíz
Añade lógica al loader de la ruta raíz que verifica si existe una preferencia de idioma almacenada y redirige en consecuencia.
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;
}
Cuando un usuario visita la ruta raíz, este loader verifica si existe una preferencia de idioma almacenada y los redirige a su ruta de idioma elegida si existe una.
5. Validar el idioma almacenado contra los locales soportados
Asegúrate de que la preferencia almacenada sea válida antes de usarla para la redirección.
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;
}
Esta validación evita redirigir a rutas de idioma inválidas o no soportadas si el valor de la cookie ha sido manipulado o si los idiomas soportados han cambiado desde que se almacenó la preferencia.