Comment charger des traductions à partir de fichiers dans React Router v7
Séparer le contenu traduisible du code
Problème
Coder en dur les chaînes de caractères destinées aux utilisateurs directement dans les composants crée un couplage étroit entre le contenu et le code. Chaque nouvelle langue nécessite que les développeurs modifient les fichiers d'implémentation, étendant la logique conditionnelle et augmentant la complexité. Lorsque le texte change, même des ajustements mineurs de formulation exigent des déploiements de code. Cette approche rend les flux de travail de traduction dépendants des cycles d'ingénierie et empêche les membres non techniques de l'équipe de gérer le contenu de manière indépendante.
À mesure que les applications se développent, les chaînes de caractères dispersées deviennent difficiles à suivre et à maintenir. Trouver chaque occurrence d'une phrase dans une base de code est sujet aux erreurs, et assurer la cohérence entre des messages similaires devient presque impossible sans une source unique de vérité.
Solution
Extraire toutes les chaînes traduisibles dans des fichiers JSON externes organisés par langue, avec un fichier par locale. Remplacer les chaînes codées en dur dans les composants par des identifiants de message qui référencent des entrées dans ces fichiers. À l'exécution, l'application charge le fichier de traduction approprié en fonction de la locale de l'utilisateur et fournit ces messages à la bibliothèque d'internationalisation, qui résout les identifiants en leurs valeurs traduites.
Cette séparation permet aux traducteurs de travailler directement avec les fichiers JSON sans toucher au code, permet des mises à jour de contenu par de simples modifications de fichiers, et fournit une source unique de vérité pour les chaînes de chaque langue.
Étapes
1. Créer des fichiers de traduction pour chaque locale
Organisez les fichiers de traduction dans un répertoire dédié, avec un fichier JSON par langue. Structurez chaque fichier comme un objet plat associant des identifiants de message à des chaînes traduites.
{
"welcome.title": "Bienvenue",
"welcome.subtitle": "Continuez là où vous vous êtes arrêté",
"nav.home": "Accueil",
"nav.about": "À propos",
"nav.contact": "Contact"
}
Enregistrez ceci comme app/translations/en.json pour l'anglais, puis créez des fichiers parallèles comme app/translations/es.json et app/translations/fr.json avec des traductions pour d'autres langues. Utilisez des clés cohérentes dans tous les fichiers afin que le même identifiant renvoie à la traduction appropriée dans chaque locale.
2. Charger les traductions dans un chargeur de route
Utilisez un chargeur de route pour récupérer le fichier de traduction pour la locale actuelle avant le rendu. Cela garantit que les messages sont disponibles lors du montage des composants.
import type { Route } from "./+types/root";
import enMessages from "./translations/en.json";
import esMessages from "./translations/es.json";
import frMessages from "./translations/fr.json";
const messages: Record<string, Record<string, string>> = {
en: enMessages,
es: esMessages,
fr: frMessages,
};
export async function loader({ request }: Route.LoaderArgs) {
const url = new URL(request.url);
const locale = url.searchParams.get("locale") || "en";
return {
locale,
messages: messages[locale] || messages.en,
};
}
Le chargeur lit la locale à partir du paramètre de requête URL et renvoie à la fois la locale et ses messages correspondants. Les composants peuvent accéder à ces données via loaderData pour configurer le fournisseur d'internationalisation.
3. Configurer l'IntlProvider avec les messages chargés
Enveloppez votre application avec IntlProvider de react-intl, en passant la locale et les messages à partir des données du chargeur.
import { IntlProvider } from "react-intl";
import { Outlet } from "react-router";
import type { Route } from "./+types/root";
export default function Root({ loaderData }: Route.ComponentProps) {
return (
<IntlProvider locale={loaderData.locale} messages={loaderData.messages}>
<html lang={loaderData.locale}>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<Outlet />
</body>
</html>
</IntlProvider>
);
}
L'IntlProvider rend la locale et les messages disponibles pour tous les composants descendants via le contexte React. Les routes enfants sont rendues via l'Outlet et héritent de l'accès aux données de traduction.
4. Référencer les messages par identifiant dans les composants
Remplacez les chaînes codées en dur par des composants FormattedMessage qui référencent les identifiants de message de vos fichiers de traduction.
import { FormattedMessage } from "react-intl";
export default function Welcome() {
return (
<div>
<h1>
<FormattedMessage id="welcome.title" />
</h1>
<p>
<FormattedMessage id="welcome.subtitle" />
</p>
<nav>
<a href="/">
<FormattedMessage id="nav.home" />
</a>
<a href="/about">
<FormattedMessage id="nav.about" />
</a>
<a href="/contact">
<FormattedMessage id="nav.contact" />
</a>
</nav>
</div>
);
}
Chaque composant FormattedMessage recherche son id dans l'objet messages fourni par IntlProvider et affiche la chaîne traduite correspondante. Lorsque la locale change et que le chargeur s'exécute à nouveau avec des messages différents, tous les composants affichent automatiquement les nouvelles traductions sans modifications de code.