# General Translation Knowledge Base: ¿Cómo doy soporte a idiomas RTL (right-to-left) en CSS?
URL: https://generaltranslation.com/es/kb/right-to-left-css.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Coloca dir="rtl" y lang en el elemento html, ya que lang por sí solo no cambia la dirección, y luego sustituye el CSS físico, como margin-left, por propiedades lógicas, como margin-inline-start. Las filas flex, las columnas de grid y la alineación de texto predeterminada se invierten solas; translateX(), left y right, no.
Category: Formato según la configuración regional
Last updated: 2026-10-20
Authors: Kevin Wu, Jackie Chen
Published: 2026-10-20

El árabe, el hebreo, el persa y el urdu se leen de derecha a izquierda. En cuanto el documento declara esa dirección, el navegador invierte automáticamente las filas flex, las columnas de grid y la alineación de texto predeterminada. La mayoría de los errores de RTL se deben a CSS que hace referencia a un lado físico: `left`, `right`, `margin-left`, `translateX`.

## Establece la dirección en HTML

Indica la dirección y el idioma en el elemento raíz:

```html
<html lang="ar" dir="rtl">
```

La [guía del W3C sobre la dirección del texto](https://www.w3.org/International/questions/qa-html-dir) recomienda declarar la dirección base en el marcado, y no con la propiedad CSS `direction`, para que se mantenga aunque los estilos no se carguen; la [referencia de `direction`](https://developer.mozilla.org/en-US/docs/Web/CSS/direction) de MDN coincide en ello. El atributo `lang` no establece la dirección, ni siquiera con una subetiqueta de sistema de escritura como `ar-Arab`, por lo que necesitas los dos. Añade `dir` en niveles inferiores de la página solo cuando un bloque cambie de dirección, como una cita en inglés dentro de un artículo en árabe.

Para obtener `dir` a partir de un código de configuración regional en JavaScript, [`getTextInfo()` de `Intl.Locale`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getTextInfo) devuelve la dirección: `new Intl.Locale('ar').getTextInfo().direction` es `'rtl'`. Chrome 130, Safari 17 y Firefox 153 lo admiten, y las versiones de Chrome 99 a 129 exponen los mismos datos mediante una propiedad `textInfo`, así que conserva una alternativa para los navegadores más antiguos.

## ¿Qué enfoque deberías usar para invertir el layout?

Una vez establecida la dirección, falta conseguir que la hoja de estilos la respete. Hay cuatro formas habituales de hacerlo:

| Enfoque                                                                                        | Cómo se invierte el layout                                                  | Mantenimiento                                                | Riesgo principal                                                  |
| ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------- |
| `direction: rtl` de CSS en lugar de `dir`                                                      | El texto y las propiedades lógicas se invierten; las propiedades físicas no | Bajo                                                         | `:dir()` lo ignora, y la dirección se pierde sin CSS              |
| Hoja de estilos RTL independiente, escrita a mano o generada con [RTLCSS](https://rtlcss.com/) | Una segunda hoja de estilos intercambia los lados físicos                   | Dos hojas de estilos que distribuir y mantener sincronizadas | Las anulaciones se desfasan a medida que cambian los estilos LTR  |
| Atributo `dir` más propiedades lógicas de CSS                                                  | Cada propiedad se resuelve en el lado correcto a partir de `dir`            | Una sola hoja de estilos                                     | Propiedades físicas que se vuelven a colar en cambios posteriores |
| Utilidades lógicas de Tailwind CSS más las variantes `rtl:` y `ltr:`                           | Las utilidades se compilan en propiedades lógicas                           | Un solo conjunto de clases                                   | Variantes usadas donde bastaría una utilidad lógica               |

Usa propiedades lógicas (directamente o mediante Tailwind) en el código nuevo y en cualquier componente que modifiques; así, una sola hoja de estilos sirve para ambas direcciones. Recurre a una hoja de estilos RTL generada cuando una base de código heredada de gran tamaño esté plagada de `left` y `right` y necesites RTL antes de poder refactorizarla.

## Escribe el layout con propiedades lógicas

Las [propiedades lógicas de CSS](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values) nombran los lados según el flujo del texto: inline-start es la izquierda en LTR y la derecha en RTL. Tienen el estado Baseline widely available: `inset-inline-start`, por ejemplo, funciona en todos los navegadores desde abril de 2021. Así queda una tarjeta antes y después del cambio:

```css
/* Antes: los lados físicos no se invierten en RTL */
.card {
  margin-left: 16px;
  padding-right: 24px;
  text-align: left;
  border-left: 4px solid var(--accent);
  border-top-left-radius: 8px;
}
.card .badge {
  position: absolute;
  top: 8px;
  right: 8px;
}

/* Después: los lados lógicos se invierten según el atributo dir */
.card {
  margin-inline-start: 16px;
  padding-inline-end: 24px;
  text-align: start;
  border-inline-start: 4px solid var(--accent);
  border-start-start-radius: 8px;
}
.card .badge {
  position: absolute;
  inset-block-start: 8px;
  inset-inline-end: 8px;
}
```

En una página con `dir="rtl"`, `margin-inline-start: 16px` equivale a un margen derecho de 16px, y `border-start-start-radius` redondea la esquina superior derecha. Usa `margin-inline` y `padding-inline` para definir ambos lados del eje inline a la vez.

### Qué se invierte automáticamente

* **Filas de Flexbox:** con `flex-direction: row`, el primer elemento queda en el borde derecho.
* **Columnas de Grid:** la columna 1 es la situada más a la derecha.
* **Alineación del texto:** el valor inicial de `text-align` es `start`, así que el texto sin estilos se alinea a la derecha.
* **Contenido en línea y columnas de tabla:** ambos siguen la dirección base.

### Lo que no se invierte

* `left` y `right` en elementos posicionados: `left: 0` se queda a la izquierda.
* `margin-left`, `padding-right`, `border-left`, `float: left` y `text-align: left`.
* `transform: translateX()`: un valor positivo siempre desplaza hacia la derecha, por lo que los paneles deslizantes y los carruseles se mueven en sentido contrario.
* `background-position: left`, `linear-gradient(to right, …)` y el desplazamiento horizontal de `box-shadow`. Como no tienen equivalente lógico, debes reemplazarlos dentro de `:dir(rtl)`.

Para las transformaciones, invierte el signo con una propiedad personalizada:

```css
:root { --inline-sign: 1; }
:root:dir(rtl) { --inline-sign: -1; }

/* Un panel lateral que se oculta por su borde inline-start */
.drawer[data-state="closed"] {
  transform: translateX(calc(-100% * var(--inline-sign)));
}
```

### Tailwind CSS

En Tailwind CSS v4.3, las utilidades lógicas son `ms-*` y `me-*` (margen), `ps-*` y `pe-*` (relleno), `inset-s-*` e `inset-e-*`, `text-start`, `border-s-*` y `rounded-s-*`; aparecen en la página de cada utilidad, como [margin](https://tailwindcss.com/docs/margin). Las [variantes `rtl:` y `ltr:`](https://tailwindcss.com/docs/hover-focus-and-other-states#rtl-support) se compilan en selectores que incluyen `:dir()`, por lo que `ltr:` también coincide con páginas que nunca establecen `dir="ltr"`. Es preferible usar `ms-3` en lugar de `ltr:ml-3 rtl:mr-3` y reservar las variantes para transformaciones e iconos, como `rtl:-scale-x-100`.

## Gestiona iconos, texto con dirección mixta y fuentes

### Refleja solo los íconos direccionales

Invierte un ícono horizontalmente en RTL:

```css
.icon-directional:dir(rtl) {
  transform: scaleX(-1);
}
```

La [pseudoclase `:dir()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:dir) coincide con la dirección que hereda un elemento, mientras que `[dir="rtl"]` solo coincide con el elemento que tiene el atributo. `:dir()` está disponible en todos los navegadores desde diciembre de 2023 y ya tiene el estado Baseline widely available. Si das soporte a navegadores más antiguos, usa `[dir="rtl"] .icon-directional` en su lugar.

Las [directrices de right-to-left](https://developer.apple.com/design/human-interface-guidelines/right-to-left) de Apple también sirven como un buen conjunto de reglas para la web:

* **Reflejar:** flechas de atrás y adelante, controles de siguiente y anterior, barras de progreso y controles deslizantes, iconos que representan líneas de texto e iconos que indican movimiento hacia delante.
* **No reflejar:** logotipos, símbolos universales como la marca de verificación, objetos físicos como relojes, herramientas dibujadas para diestros, y fotos o ilustraciones.
* **Nunca invertir los dígitos:** un número de teléfono o el número 541 se leen en el mismo orden en todos los idiomas.

### Aislar texto bidireccional

Cuando un texto RTL y otro LTR comparten una línea, los caracteres neutros, como espacios, guiones y números, pueden quedar asociados al lado equivocado; la [guía de marcado bidi inline](https://www.w3.org/International/articles/inline-bidi-markup/) del W3C llama a este fenómeno «spillover» (desbordamiento). En el [ejemplo de `<bdi>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdi) de MDN, un nombre en árabe seguido de &quot;- 1st place&quot; se renderiza con el &quot;1&quot; en el extremo equivocado. Envuelve cada valor cuya dirección difiera de la del texto que lo rodea:

```html
<!-- Un nombre de su base de datos, de dirección desconocida -->
<li><bdi>اَلأَعْشَى</bdi> - 1st place</li>

<!-- Un valor que siempre es LTR dentro de un texto en árabe -->
<p>اتصل بنا على <span dir="ltr">+1 (555) 010-2030</span></p>

<!-- Texto escrito por el usuario, de dirección desconocida -->
<textarea dir="auto"></textarea>
```

`<bdi>` se comporta como `dir="auto"` y toma su dirección del primer carácter fuerte. Usa `dir="ltr"` en los valores que siempre van de izquierda a derecha (left-to-right): números de teléfono, direcciones de correo electrónico, URL e ID de pedido. La propiedad CSS `unicode-bidi: isolate` consigue el mismo aislamiento desde una hoja de estilos, pero MDN [desaconseja a los autores reemplazar `unicode-bidi`](https://developer.mozilla.org/en-US/docs/Web/CSS/unicode-bidi), así que es preferible usar `dir` y `<bdi>` en el marcado.

### Fuentes

* **Cubre el sistema de escritura.** Asigna por idioma una fuente con glifos árabes o hebreos: `:lang(ar) { font-family: "Noto Sans Arabic", system-ui, sans-serif; }`.
* **No espacies las letras en árabe.** Sus letras se enlazan, y la [especificación CSS Text](https://www.w3.org/TR/css-text-3/) recomienda a los autores evitar `letter-spacing` en sistemas de escritura cursivos porque los resultados no son interoperables.
* **Compensa las mayúsculas.** El árabe y el hebreo no tienen mayúsculas, así que el texto latino en mayúsculas a su lado parece más grande; Apple sugiere aumentar unos 2 puntos el tamaño de la fuente RTL.

## Errores comunes de RTL y cómo solucionarlos

### La página está traducida, pero sigue leyéndose de izquierda a derecha

**Causa:** la página traducida define `lang="ar"` pero no `dir`, o aplica `direction: rtl` a un contenedor interno. **Solución:** define `dir="rtl"` en `<html>` según la configuración regional activa en cada renderizado, incluido el HTML server-rendered.

### El espaciado queda en el lado incorrecto de los iconos y las etiquetas

**Causa:** `margin-left` o `padding-right` entre un icono y su texto. **Solución:** cambia a `margin-inline-end` o a `me-*` de Tailwind, o usa `gap` en un contenedor flex, que no depende de ningún lado.

Según mediciones en Chrome 155 en un botón inline-flex con un icono de 16px delante de su etiqueta, el margen físico no deja ningún espacio en texto right-to-left y desplaza sus 8px al borde exterior, mientras que el margen lógico se coloca en el lado orientado hacia la etiqueta:

```text title="Output"
.icon { margin-right: 8px }        ltr: 8px gap   rtl: 0px gap
.icon { margin-inline-end: 8px }   ltr: 8px gap   rtl: 8px gap (computed as margin-left)
```

### Las insignias, los tooltips y los menús desplegables se abren desde el lado incorrecto

**Causa:** `position: absolute` con `left` o `right`, o una biblioteca de posicionamiento de JavaScript que calcula la posición desde la izquierda. **Solución:** usa `inset-inline-start` e `inset-inline-end`, y pasa la dirección a la biblioteca de posicionamiento si lo admite.

### Los paneles laterales y los carruseles se deslizan hacia el lado equivocado

**Causa:** `translateX()` usa coordenadas físicas. **Solución:** multiplica por un signo de dirección, como en el ejemplo del panel lateral anterior, y prueba los gestos de deslizamiento en RTL.

### La flecha de retroceso apunta hacia adelante o el logotipo aparece reflejado

**Causa:** los iconos se invierten todos o ninguno. **Solución:** refleja únicamente los iconos direccionales.

### La puntuación o los números saltan al otro extremo de la línea

**Causa:** un valor insertado cuya dirección es distinta de la del texto que lo rodea, como un nombre de usuario, un nombre de producto o un número de teléfono. **Solución:** envuélvelo en `<bdi>` o en un elemento con `dir="ltr"` si sabes que es left-to-right.

### Las letras árabes aparecen desconectadas

**Causa:** un `letter-spacing` global pensado para encabezados en alfabeto latino. **Solución:** restablece `letter-spacing` a 0 para `:lang(ar)` y otros sistemas de escritura cursivos.

Para detectar estos errores antes de que existan las traducciones, recorre la aplicación con una [pseudoconfiguración regional](/kb/pseudo-localization) right-to-left y, antes del lanzamiento, realiza la comprobación de right-to-left de la [lista de verificación para el lanzamiento de aplicaciones multilingües](/kb/multilingual-app-launch-checklist).

## Establecer dir a partir de la configuración regional activa con General Translation

En una aplicación de Next.js con App Router, `gt-next` expone [`getLocaleDirection`](/docs/react/nextjs/reference/functions/get-locale-direction) desde `gt-next/server`, de modo que el layout raíz puede establecer `lang` y `dir` en el servidor. En este ejemplo se supone que `app/[locale]/layout.tsx` es el layout raíz, que, según Next.js, [debe renderizar tanto `<html>` como `<body>`](https://nextjs.org/docs/app/api-reference/file-conventions/layout#root-layout):

```tsx title="app/[locale]/layout.tsx"
import { getLocale, getLocaleDirection } from 'gt-next/server';
import type { ReactNode } from 'react';

export default async function LocaleLayout({ children }: { children: ReactNode }) {
  const [locale, direction] = await Promise.all([
    getLocale(),
    getLocaleDirection(),
  ]);
  return (
    <html lang={locale} dir={direction}>
      <body>{children}</body>
    </html>
  );
}
```

En los componentes de `gt-react`, `gt-next` y `gt-react-native`, el hook [`useLocaleDirection`](/docs/react/reference/hooks/use-locale-direction) devuelve `'ltr'` o `'rtl'` según la configuración regional activa. En la web, asigna ese valor a `document.documentElement.dir` desde un componente de tu árbol. React Native no tiene `document`: la dirección del layout nativo la determina [`I18nManager`](https://reactnative.dev/docs/i18nmanager) de React Native, y cualquier cambio mediante `allowRTL` o `forceRTL` solo surte efecto la próxima vez que se inicie la app. Para otras plataformas, como Vue, `gt-tanstack-start` o las plantillas de servidor, la función [`getLocaleDirection`](/docs/platform/core/reference/utility-functions/locales/get-locale-direction) de la biblioteca Core, incluida en `generaltranslation`, se ejecuta localmente sin necesidad de clave de API y devuelve `'rtl'` para códigos como `ar-SA`, `he-IL` y `fa`. En cualquier caso, tu CSS sigue necesitando las propiedades lógicas y las reglas de iconos descritas anteriormente.

## Conclusiones clave

* Añade `lang` y `dir="rtl"` a `<html>`; `lang` por sí solo no cambia la dirección, y no se debe delegar esa tarea a la propiedad CSS `direction`.
* Las filas flex, las columnas de grid y la alineación de texto predeterminada se invierten automáticamente; `left`, `right`, `margin-left`, `translateX()`, `background-position` y los desplazamientos de sombra, no.
* Sustituye las propiedades físicas por propiedades lógicas (`margin-inline-start`, `inset-inline-end`, `text-align: start`, `border-start-start-radius`), o por `ms-*`, `ps-*` e `inset-s-*` de Tailwind.
* Refleja las flechas y los indicadores de progreso con `:dir(rtl)`, pero nunca los logotipos, las marcas de verificación ni los relojes, y envuelve los valores de dirección mixta en `<bdi>` o `dir="ltr"`.
* En los SDK de General Translation, establece `dir` a partir de [`getLocaleDirection`](/docs/react/nextjs/reference/functions/get-locale-direction) (en código de servidor de Next.js o con la biblioteca Core) o de `useLocaleDirection` (en React).

## Related questions

- [Lista de comprobación de pruebas de localización para aplicaciones multilingües](https://generaltranslation.com/es/kb/multilingual-app-launch-checklist.mdx): Prueba nueve aspectos en cada configuración regional compatible en una compilación de producción: recorridos completos, incluidos los estados de error y los correos electrónicos; cambio de idioma; plurales; moneda y zonas horarias; expansión del texto; diseño right-to-left; accesibilidad; entrega de traducciones; y URL de idioma rastreables. Asigna a cada comprobación un criterio de aprobación explícito.
- [¿Qué es la pseudolocalización?](https://generaltranslation.com/es/kb/pseudo-localization.mdx): La pseudolocalización es una prueba que sustituye tu texto de origen por una versión alterada (con acentos, alrededor de un 40 % más larga y entre corchetes) para que las cadenas codificadas de forma fija, el truncamiento, las fuentes rotas y las oraciones concatenadas salgan a la luz antes de que exista ninguna traducción. Android incluye para ello las pseudoconfiguraciones regionales en-XA y ar-XB.
- [¿Cómo gestiono los plurales en React?](https://generaltranslation.com/es/kb/how-to-handle-plurals-in-react.mdx): Para gestionar los plurales en React, asigna a cada idioma una oración por cada forma plural y selecciónala con las reglas de plural de ese idioma, ya que el inglés tiene 2 formas, el ruso tiene 4 y el árabe tiene 6. El componente `<Plural>` de General Translation en gt-react y gt-next recibe tus formas de origen, y General Translation genera las formas que necesita cada idioma de destino.
- [¿Puedo hacer i18n sin archivos de traducción?](https://generaltranslation.com/es/kb/i18n-without-translation-files.mdx): Sí. Las bibliotecas gt-react y gt-next de General Translation permiten hacer i18n en React y Next.js sin claves de traducción y sin tener que mantener un archivo JSON por idioma. El texto permanece inline en JSX dentro del componente `<T>`, y el comando gt translate genera las traducciones de cada idioma durante la compilación, con contexto extraído de tu código. Locadex puede mantenerlas al día mediante pull requests.

## Previous and next questions

- Previous question: [¿Cómo detecto el idioma del navegador de un usuario?](https://generaltranslation.com/es/kb/detect-user-language.mdx)
- Next question: [¿Cómo encuentro cadenas codificadas de forma fija que no están traducidas?](https://generaltranslation.com/es/kb/find-hardcoded-strings.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/es/kb/sitemap.md) for every article.
