Comment construire un composant de sélection de langue dans React Router v7

Changer de langue tout en restant sur la même page

Problème

Les utilisateurs s'attendent à ce que les sélecteurs de langue préservent leur contexte actuel. Lorsqu'ils naviguent sur une page de produit, un article d'aide ou les paramètres de compte, le passage de l'anglais à l'espagnol devrait afficher cette même page en espagnol. Au lieu de cela, de nombreuses implémentations traitent la sélection de langue comme un événement de navigation qui redirige les utilisateurs vers la page d'accueil dans la nouvelle langue, les forçant à revenir là où ils étaient. Cela interrompt le flux de l'utilisateur et crée de la frustration, particulièrement dans les applications riches en contenu où les utilisateurs peuvent se trouver profondément dans une hiérarchie de navigation.

La cause principale est que les sélecteurs de langue utilisent souvent des URLs de destination codées en dur plutôt que de construire dynamiquement des URLs basées sur la page actuelle. Sans lire et transformer la structure d'URL actuelle, le sélecteur ne peut pas maintenir la position de l'utilisateur dans l'application lors des changements de langue.

Solution

Construire un composant de sélection de langue qui lit l'URL actuelle et extrait à la fois le paramètre de locale active et les segments de chemin restants. Pour chaque langue prise en charge, générer une nouvelle URL en remplaçant uniquement le segment de locale tout en conservant tous les autres segments de chemin et paramètres de requête intacts. Afficher ces URLs sous forme de liens pour que les utilisateurs puissent changer de langue sans perdre leur place dans l'application.

Cette approche traite la locale comme un paramètre remplaçable dans la structure d'URL plutôt que comme une destination de navigation, garantissant que le passage de /en/products/shoes à /es/products/shoes préserve le contexte de l'utilisateur.

Étapes

1. Créer une fonction auxiliaire pour construire des URLs adaptées à la locale

Définir une fonction qui prend le chemin actuel et une locale cible, puis construit un nouveau chemin en remplaçant le segment de locale.

export function buildLocalePath(
  currentPath: string,
  newLocale: string,
): string {
  const segments = currentPath.split("/").filter(Boolean);

  if (segments.length === 0) {
    return `/${newLocale}`;
  }

  segments[0] = newLocale;
  return `/${segments.join("/")}`;
}

Cette fonction divise le chemin en segments, remplace le premier segment par la locale cible, et reconstruit le chemin. Elle gère les cas particuliers comme le chemin racine et s'assure que la locale est toujours le premier segment.

2. Définissez vos locales prises en charge

Créez un objet de configuration qui liste toutes les langues prises en charge par votre application.

export const locales = [
  { code: "en", label: "English" },
  { code: "es", label: "Español" },
  { code: "fr", label: "Français" },
  { code: "de", label: "Deutsch" },
];

Cette configuration sert de source de vérité pour les langues à afficher dans le sélecteur et fournit des libellés conviviaux pour chaque locale.

3. Construisez le composant de sélection de langue

Créez un composant qui lit l'emplacement actuel, détermine la locale active et affiche des liens pour toutes les autres langues prises en charge.

import { Link, useLocation, useParams } from "react-router";
import { locales, buildLocalePath } from "./i18n-config";

export function LanguageSwitcher() {
  const location = useLocation();
  const params = useParams();
  const currentLocale = params.locale || "en";

  return (
    <nav aria-label="Language switcher">
      <ul>
        {locales.map((locale) => {
          const isActive = locale.code === currentLocale;
          const newPath = buildLocalePath(location.pathname, locale.code);

          return (
            <li key={locale.code}>
              {isActive ? (
                <span aria-current="true">{locale.label}</span>
              ) : (
                <Link to={newPath}>{locale.label}</Link>
              )}
            </li>
          );
        })}
      </ul>
    </nav>
  );
}

Le composant utilise useLocation pour accéder au chemin actuel et useParams pour extraire la locale active de l'URL. Pour chaque locale prise en charge, il génère un nouveau chemin en utilisant la fonction auxiliaire et affiche soit un lien, soit un élément non interactif pour la langue actuelle.

4. Préservez les paramètres de requête et les fragments d'URL

Étendez la fonction auxiliaire pour maintenir les chaînes de requête et les fragments d'URL lors du changement de langue.

export function buildLocalePath(
  currentPath: string,
  search: string,
  hash: string,
  newLocale: string,
): string {
  const segments = currentPath.split("/").filter(Boolean);

  if (segments.length === 0) {
    return `/${newLocale}${search}${hash}`;
  }

  segments[0] = newLocale;
  return `/${segments.join("/")}${search}${hash}`;
}

Cette version mise à jour accepte les propriétés search et hash de l'objet location et les ajoute au chemin généré, garantissant ainsi que les filtres, les paramètres de tri et les liens d'ancrage survivent au changement de langue.

5. Mettre à jour le composant pour utiliser l'assistant amélioré

Modifiez le sélecteur pour transmettre les informations complètes de localisation à la fonction d'assistance.

import { Link, useLocation, useParams } from "react-router";
import { locales, buildLocalePath } from "./i18n-config";

export function LanguageSwitcher() {
  const location = useLocation();
  const params = useParams();
  const currentLocale = params.locale || "en";

  return (
    <nav aria-label="Language switcher">
      <ul>
        {locales.map((locale) => {
          const isActive = locale.code === currentLocale;
          const newPath = buildLocalePath(
            location.pathname,
            location.search,
            location.hash,
            locale.code,
          );

          return (
            <li key={locale.code}>
              {isActive ? (
                <span aria-current="true">{locale.label}</span>
              ) : (
                <Link to={newPath}>{locale.label}</Link>
              )}
            </li>
          );
        })}
      </ul>
    </nav>
  );
}

Le composant transmet maintenant location.search et location.hash à l'assistant, garantissant que les URL comme /en/products?category=shoes#reviews deviennent /es/products?category=shoes#reviews lors du passage à l'espagnol.