Comment mémoriser la sélection de langue entre les sessions dans React Router v7

Stocker le choix explicite de langue de l'utilisateur

Problème

Lorsqu'un utilisateur sélectionne explicitement une langue, ce choix reflète sa préférence et devrait prévaloir sur toute détection automatique. Sans persistance, cette sélection disparaît à la fermeture du navigateur ou à la fin de la session. Lors de la visite suivante, l'application redémarre à zéro, obligeant l'utilisateur à sélectionner à nouveau sa langue. Cette répétition signale que l'application ne respecte pas ses préférences, créant ainsi des frictions et diminuant la confiance.

Solution

Stocker le choix de langue de l'utilisateur dans un emplacement persistant comme un cookie lorsqu'il fait une sélection. Lors des visites ultérieures, vérifier cette préférence enregistrée avant de recourir aux en-têtes du navigateur ou à d'autres méthodes de détection. Si une langue valide est trouvée, rediriger automatiquement l'utilisateur vers la route de cette langue. Cela garantit que son choix explicite prend la priorité et persiste d'une session à l'autre.

Étapes

Définir un cookie qui contiendra la langue sélectionnée par l'utilisateur avec une longue durée d'expiration.

import { createCookie } from "react-router";

export const languagePreference = createCookie("language-preference", {
  maxAge: 31536000,
  httpOnly: false,
  secure: process.env.NODE_ENV === "production",
  sameSite: "lax",
});

Ce cookie persiste pendant un an et est accessible au code côté client pour lire la préférence.

2. Ajouter une action pour stocker la sélection de langue

Créer une action qui gère les soumissions du formulaire de sélection de langue et stocke le choix dans le 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("/");
}

Lorsque l'utilisateur soumet son choix de langue, cette action le stocke dans le cookie et le redirige vers la route de langue appropriée.

3. Créer un composant de sélection de langue

Construisez un composant de formulaire qui permet aux utilisateurs de choisir leur langue préférée.

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>
  );
}

Ce composant est soumis automatiquement lorsque l'utilisateur change la sélection, déclenchant l'action qui stocke la préférence.

4. Vérifier la préférence stockée dans le loader racine

Ajoutez une logique au loader de la route racine qui vérifie si une préférence de langue est stockée et redirige en conséquence.

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;
}

Lorsqu'un utilisateur visite le chemin racine, ce loader vérifie s'il existe une préférence de langue stockée et les redirige vers leur route de langue choisie si elle existe.

5. Valider la langue stockée par rapport aux locales supportées

Assurez-vous que la préférence stockée est valide avant de l'utiliser pour la redirection.

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;
}

Cette validation empêche la redirection vers des routes de langue invalides ou non supportées si la valeur du cookie a été altérée ou si les langues supportées ont changé depuis que la préférence a été stockée.