Comment charger les traductions depuis des 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 nécessitent 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.
Au fur et à mesure que les applications se développent, les littéraux de chaînes dispersés 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é centralisée.
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. Au moment de 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 les 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
Organiser les fichiers de traduction dans un répertoire dédié, avec un fichier JSON par langue. Structurer chaque fichier comme un objet plat mappant les identifiants de message aux chaînes traduites.
{
"welcome.title": "Welcome back",
"welcome.subtitle": "Continue where you left off",
"nav.home": "Home",
"nav.about": "About",
"nav.contact": "Contact"
}
Enregistrer ceci sous app/translations/en.json pour l'anglais, puis créer des fichiers parallèles comme app/translations/es.json et app/translations/fr.json avec les traductions pour d'autres langues. Utiliser des clés cohérentes dans tous les fichiers afin que le même identifiant se résolve en la traduction appropriée dans chaque locale.
2. Charger les traductions dans un loader de route
Utilisez un loader de route pour récupérer le fichier de traduction de la locale actuelle avant le rendu. Cela garantit que les messages sont disponibles lorsque les composants sont montés.
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 loader lit la locale depuis le paramètre de requête URL et retourne à 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 depuis les données du loader.
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>
);
}
Le IntlProvider rend la locale et les messages disponibles à tous les composants descendants via le contexte React. Les routes enfants s'affichent via le 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 messages 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 loader s'exécute à nouveau avec des messages différents, tous les composants affichent automatiquement les nouvelles traductions sans modification du code.