Comment valider les paramètres de langue dans les URL avec React Router v7
Gérer élégamment les codes de langue non pris en charge
Problème
Lorsque les identifiants de langue deviennent partie intégrante de la structure d'URL, ils se transforment en entrées utilisateur pouvant contenir n'importe quelle valeur arbitraire. Un utilisateur pourrait manuellement saisir /xx/about, /gibberish/contact, ou tout autre code de langue invalide dans la barre d'adresse. Sans validation, l'application doit décider comment gérer ces entrées invalides. Permettre à des langues invalides de continuer peut entraîner des traductions manquantes, un formatage défectueux ou des erreurs d'exécution lorsque les bibliothèques i18n tentent de charger des données de langue inexistantes. Revenir silencieusement à une langue par défaut sans informer l'utilisateur crée de la confusion quant à la langue qu'il consulte. Afficher des limites d'erreur ou des pages vides laisse les utilisateurs bloqués sans chemin clair pour avancer.
Le défi est amplifié par le fait que la validation de la langue doit se produire tôt dans le cycle de vie de la requête, avant que les composants ne soient rendus et avant que les données de traduction ne soient chargées. Si la validation intervient trop tard, l'application peut avoir déjà tenté de récupérer des ressources spécifiques à la langue ou initialisé des fournisseurs i18n avec une configuration invalide, gaspillant des ressources et causant potentiellement des défaillances en cascade.
Solution
Validez le paramètre de langue dans un chargeur de route avant que la page ne soit rendue. Les chargeurs dans React Router s'exécutent avant le montage des composants, ce qui en fait l'endroit idéal pour vérifier si la langue demandée existe dans la liste des langues prises en charge par votre application. Si la langue est valide, permettez à la requête de se poursuivre normalement. Si la langue est invalide, redirigez immédiatement l'utilisateur vers une solution de repli sûre — soit le même chemin avec une langue par défaut valide, soit une page dédiée "non trouvée" qui explique le problème.
Cette approche empêche les langues invalides d'atteindre vos composants et fournisseurs i18n. En renvoyant une réponse de redirection depuis le chargeur, vous exploitez le système de navigation intégré de React Router pour gérer l'erreur avec élégance. La redirection se produit sur le serveur pendant le SSR ou sur le client pendant la navigation, assurant un comportement cohérent à travers les stratégies de rendu. Les utilisateurs reçoivent un retour immédiat via le changement d'URL, et votre application évite de tenter de charger des ressources pour des langues inexistantes.
Étapes
1. Définir vos locales supportées
Créez une liste des codes de locale valides que votre application prend en charge. Cette liste sert de source de vérité pour la validation.
export const SUPPORTED_LOCALES = ["en", "es", "fr", "de", "ja"] as const;
export type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];
export function isValidLocale(locale: string): locale is SupportedLocale {
return SUPPORTED_LOCALES.includes(locale as SupportedLocale);
}
Cette fonction utilitaire fournit une validation typée et peut être réutilisée dans les loaders et autres parties de votre application.
2. Ajouter la validation au loader de votre route préfixée par la locale
Dans le module de route pour vos pages préfixées par la locale, exportez un loader qui vérifie le paramètre de locale et redirige s'il n'est pas valide.
import type { Route } from "./+types/page";
import { redirect } from "react-router";
import { isValidLocale } from "~/i18n/locales";
export async function loader({ params }: Route.LoaderArgs) {
const { locale } = params;
if (!locale || !isValidLocale(locale)) {
return redirect("/en/not-found");
}
return { locale };
}
export default function Page({ loaderData }: Route.ComponentProps) {
return (
<div>
<h1>Content in {loaderData.locale}</h1>
</div>
);
}
Le loader extrait la locale de l'URL, la valide et redirige vers une solution de repli sécurisée si la validation échoue. Si la locale est valide, il renvoie des données que les composants peuvent utiliser.
3. Configurer vos routes avec des paramètres de locale
Dans votre fichier routes.ts, définissez des routes qui incluent la locale comme segment dynamique.
import { type RouteConfig, route } from "@react-router/dev/routes";
export default [
route(":locale/about", "./routes/about.tsx"),
route(":locale/contact", "./routes/contact.tsx"),
route(":locale/not-found", "./routes/not-found.tsx"),
] satisfies RouteConfig;
Chaque route avec un paramètre :locale invoquera son loader, où la validation se produit avant que le composant ne soit rendu.
4. Créer une page not-found pour les locales invalides
Construisez une page dédiée qui explique que la locale n'a pas été trouvée et offre des options de navigation.
import { Link } from "react-router";
import { SUPPORTED_LOCALES } from "~/i18n/locales";
export default function NotFound() {
return (
<div>
<h1>Langue non trouvée</h1>
<p>La langue demandée n'est pas prise en charge.</p>
<nav>
<p>Choisissez une langue :</p>
<ul>
{SUPPORTED_LOCALES.map((locale) => (
<li key={locale}>
<Link to={`/${locale}`}>{locale.toUpperCase()}</Link>
</li>
))}
</ul>
</nav>
</div>
);
}
Cette page fournit un retour clair et des étapes suivantes exploitables, aidant les utilisateurs à se remettre de l'erreur sans quitter votre application.
5. Ajouter une route catch-all pour les chemins complètement invalides
Pour les URLs qui ne correspondent à aucun modèle de route défini, ajoutez une route splat à la fin de votre configuration de route.
import { type RouteConfig, route } from "@react-router/dev/routes";
export default [
route(":locale/about", "./routes/about.tsx"),
route(":locale/contact", "./routes/contact.tsx"),
route(":locale/not-found", "./routes/not-found.tsx"),
route("*", "./routes/catch-all.tsx"),
] satisfies RouteConfig;
La route splat correspond à tout chemin qui ne correspond pas aux routes précédentes, vous permettant de gérer les URLs complètement malformées séparément des codes de locale invalides.
6. Rediriger optionnellement vers une locale par défaut au lieu d'une page not-found
Si vous préférez corriger silencieusement les locales invalides plutôt que d'afficher une erreur, redirigez vers le même chemin avec une locale par défaut.
import type { Route } from "./+types/page";
import { redirect } from "react-router";
import { isValidLocale } from "~/i18n/locales";
const DEFAULT_LOCALE = "en";
export async function loader({ params, request }: Route.LoaderArgs) {
const { locale } = params;
if (!locale || !isValidLocale(locale)) {
const url = new URL(request.url);
const newPath = url.pathname.replace(/^\/[^/]+/, `/${DEFAULT_LOCALE}`);
return redirect(newPath);
}
return { locale };
}
Cette approche préserve le reste du chemin URL tout en remplaçant uniquement le segment de locale invalide, offrant une expérience utilisateur plus fluide lorsque la locale est le seul problème.