Comment prendre en charge les langues de droite à gauche (RTL) dans React Router v7
Mise en miroir des layouts pour l'arabe et l'hébreu
Problème
La plupart des systèmes de design supposent que le texte s'écoule de gauche à droite. La navigation commence à gauche, les barres latérales s'ancrent à gauche et le contenu se lit de gauche à droite. Mais l'arabe et l'hébreu se lisent de droite à gauche, et leurs mises en page devraient être inversées en conséquence—ce qui apparaît à gauche pour l'anglais devrait apparaître à droite pour l'arabe. Sans cette inversion, l'interface entière semble à l'envers. Le flux visuel contredit le sens de lecture, créant une expérience désorientante où les utilisateurs doivent lutter contre la mise en page pour lire naturellement.
Le défi s'étend au-delà du simple alignement du texte. Les marges, le rembourrage, les bordures et le positionnement doivent tous s'adapter. Un bouton avec une marge à gauche en anglais devrait avoir une marge à droite en arabe. Les icônes qui pointent vers la droite devraient pointer vers la gauche. Toute la logique spatiale de l'interface doit s'inverser pour correspondre au sens de lecture.
Solution
Définissez l'attribut dir sur l'élément <html> du document pour spécifier la direction du texte pour la langue actuelle. Utilisez ltr pour les langues de gauche à droite comme l'anglais et rtl pour les langues de droite à gauche comme l'arabe et l'hébreu. Ce seul attribut permet aux navigateurs d'inverser automatiquement de nombreux comportements de mise en page.
Concevez des mises en page en utilisant des propriétés logiques CSS comme margin-inline-start au lieu de propriétés physiques comme margin-left, afin que l'espacement s'adapte automatiquement lorsque la direction du texte change. Les propriétés logiques sont indépendantes de la direction—elles définissent l'espacement par rapport au flux du texte plutôt qu'à des positions fixes à l'écran. Lorsque la direction du document est RTL, margin-inline-start devient margin-right, et la mise en page s'inverse d'elle-même sans code supplémentaire.
Étapes
1. Détecter la direction du texte de la locale actuelle
React Router 7 nécessite une route racine dans app/root.tsx qui rend le document HTML. Créez une fonction d'aide qui associe les codes de locale à leur direction de texte.
const locales = {
en: { dir: "ltr" },
ar: { dir: "rtl" },
he: { dir: "rtl" },
es: { dir: "ltr" },
};
function getTextDirection(locale: string): "ltr" | "rtl" {
return locales[locale as keyof typeof locales]?.dir || "ltr";
}
Cette fonction renvoie la direction appropriée pour chaque locale prise en charge, avec une valeur par défaut de gauche à droite pour les locales inconnues.
2. Définir l'attribut dir sur l'élément html
Dans votre mise en page racine, récupérez la locale actuelle et appliquez la direction correspondante au document.
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "react-router";
export function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" dir="ltr">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Links />
</head>
<body>
{children}
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}
export default function Root() {
return <Outlet />;
}
Remplacez le dir="ltr" codé en dur par une valeur dynamique basée sur votre mécanisme de détection de locale. Si votre application stocke la locale actuelle dans un loader ou un contexte, lisez-la ici et transmettez-la à getTextDirection.
3. Utiliser des propriétés logiques pour l'espacement
Remplacez les propriétés CSS physiques par leurs équivalents logiques dans vos composants et feuilles de style.
export default function Card({
title,
children,
}: {
title: string;
children: React.ReactNode;
}) {
return (
<div
style={{
paddingInlineStart: "1rem",
paddingInlineEnd: "1rem",
marginInlineStart: "auto",
borderInlineStart: "4px solid blue",
}}
>
<h2>{title}</h2>
{children}
</div>
);
}
Lorsque dir="rtl" est défini, paddingInlineStart s'applique au côté droit et paddingInlineEnd au côté gauche, reflétant automatiquement la mise en page. Le même composant fonctionne correctement dans les contextes LTR et RTL sans logique conditionnelle.
4. Appliquer des propriétés logiques aux conteneurs de mise en page
Utilisez des propriétés logiques pour les modèles de mise en page courants comme les barres de navigation et les grilles de contenu.
export default function Navigation() {
return (
<nav
style={{
display: "flex",
gap: "1rem",
paddingInline: "2rem",
borderBlockEnd: "1px solid #ccc",
}}
>
<a href="/" style={{ marginInlineEnd: "auto" }}>
Home
</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
);
}
paddingInline définit le rembourrage des deux côtés inline, et marginInlineEnd: "auto" pousse le contenu vers le bord inline-start, qui bascule de gauche à droite lorsque la direction change. La mise en page de la navigation se reflète automatiquement pour les langues RTL.
5. Gérer les icônes et les graphiques directionnels
Pour les icônes qui représentent une direction ou un flux, inversez-les conditionnellement en fonction de la direction du texte.
function BackButton() {
const dir = document.documentElement.dir;
const iconStyle = {
transform: dir === "rtl" ? "scaleX(-1)" : "none",
marginInlineEnd: "0.5rem",
};
return (
<button>
<span style={iconStyle}>←</span>
Back
</button>
);
}
Cela inverse horizontalement l'icône de flèche en mode RTL tout en gardant le texte et l'espacement adaptés à la direction grâce aux propriétés logiques. Toutes les icônes n'ont pas besoin d'être inversées—seulement celles qui indiquent une direction ou un mouvement.