Cómo soportar idiomas de derecha a izquierda (RTL) en TanStack Start v1
Reflejar diseños para árabe y hebreo
Problema
La mayoría de los diseños web asumen que el texto fluye de izquierda a derecha. Los menús de navegación se anclan a la izquierda, las barras laterales aparecen a la izquierda y el contenido se lee de izquierda a derecha. Para idiomas como el árabe y el hebreo que se leen de derecha a izquierda, esto crea una experiencia desorientadora donde el flujo visual contradice la dirección de lectura. Los usuarios ven la navegación en el lado incorrecto, iconos posicionados de forma extraña y diseños que se sienten al revés. Sin un soporte RTL adecuado, la interfaz se vuelve difícil de navegar y comprender para los hablantes de idiomas RTL.
Solución
Establecer la dirección del texto del documento dinámicamente según la configuración regional actual, permitiendo que el navegador refleje automáticamente el diseño para idiomas RTL. Usar propiedades lógicas de CSS en lugar de propiedades direccionales físicas para que el espaciado, posicionamiento y alineación se adapten a la dirección del texto sin código adicional. Este enfoque permite que los diseños permanezcan agnósticos a la dirección: lo que aparece al "inicio" del contenido en inglés aparecerá correctamente al "inicio" (lado derecho) en árabe, con el navegador manejando la transformación.
Pasos
1. Determinar la dirección del texto desde la configuración regional
Crear una función auxiliar que devuelva la dirección del texto para una configuración regional dada utilizando la API de internacionalización integrada del navegador.
export function getTextDirection(locale: string): "ltr" | "rtl" {
try {
const localeObj = new Intl.Locale(locale);
if (
"getTextInfo" in localeObj &&
typeof localeObj.getTextInfo === "function"
) {
return localeObj.getTextInfo().direction;
}
} catch (e) {
console.warn(`Could not determine direction for locale: ${locale}`);
}
const rtlLocales = ["ar", "he", "fa", "ur"];
const lang = locale.split("-")[0];
return rtlLocales.includes(lang) ? "rtl" : "ltr";
}
Esta función utiliza Intl.Locale.getTextInfo() cuando está disponible y recurre a una lista de idiomas RTL conocidos. Devuelve 'ltr' o 'rtl' según la configuración regional.
2. Establecer el atributo dir en el elemento html
En tu ruta raíz, recupera la configuración regional actual y aplica la dirección correspondiente al elemento <html> del documento.
import {
createRootRoute,
Outlet,
Scripts,
HeadContent,
} from "@tanstack/react-router";
import { useIntl } from "react-intl";
import { getTextDirection } from "~/utils/text-direction";
export const Route = createRootRoute({
component: RootComponent,
});
function RootComponent() {
return (
<RootDocument>
<Outlet />
</RootDocument>
);
}
function RootDocument({ children }: { children: React.ReactNode }) {
const intl = useIntl();
const dir = getTextDirection(intl.locale);
return (
<html lang={intl.locale} dir={dir}>
<head>
<HeadContent />
</head>
<body>
{children}
<Scripts />
</body>
</html>
);
}
El atributo dir en el elemento <html> indica al navegador que invierta el diseño para idiomas RTL. Flexbox, grid y el contenido en línea se reflejan automáticamente.
3. Reemplaza las propiedades CSS físicas con propiedades lógicas
Actualiza tus hojas de estilo para usar propiedades lógicas que respondan a la dirección del texto en lugar de direcciones físicas fijas.
.sidebar {
padding-inline-start: 1rem;
margin-inline-end: 2rem;
border-inline-start: 1px solid #ccc;
}
.icon {
margin-inline-end: 0.5rem;
}
.card {
inset-inline-start: 0;
text-align: start;
}
Las propiedades lógicas como padding-inline-start se asignan a padding-left en LTR y a padding-right en RTL. El navegador aplica la propiedad física correcta según el atributo dir, por lo que tus estilos funcionan en ambas direcciones sin duplicación.
4. Usa valores de alineación independientes de la dirección
Reemplaza las palabras clave de alineación física con las lógicas en tu CSS y estilos en línea.
export function Header() {
return (
<header
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<nav style={{ display: "flex", gap: "1rem" }}>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
<div style={{ textAlign: "end" }}>
<button>Menu</button>
</div>
</header>
);
}
Usar textAlign: 'end' en lugar de 'right' garantiza que el texto se alinee al final de la dirección de lectura. Las propiedades de flexbox como justifyContent y alignItems respetan automáticamente la dirección establecida por el atributo dir.