Der arabische Build sieht kaputt aus, dabei ist die Übersetzung völlig in Ordnung. Die Sidebar sitzt links, der Zurück-Pfeil zeigt nach links, der Avatar klebt am linken Rand jeder Karte – jedes Wort ist Arabisch, jede Box steht genau da, wo die englische Version sie zurückgelassen hat. Textrichtung ist ein HTML-Attribut plus eine Reihe von CSS-Entscheidungen, die in deinen Komponenten liegen, und keine Übersetzungsdatei trägt diese Information. globalize.now ist KI-gestützte Lokalisierungsinfrastruktur, die die Strings extrahiert und die Kataloge bei jedem Git-Push synchron hält.
Warum liest sich meine Lovable-App auf Arabisch immer noch von links nach rechts?
Weil die Richtung eine Eigenschaft des Dokuments und des Stylesheets ist, nicht der Nachricht.
Deine Kataloge bestehen aus Paaren von Quell-Strings und übersetzten Strings. Sobald sie für ar kompilieren und deine Komponenten sie rendern, ändern sich die Wörter und sonst nichts. Ob diese Kataloge Lingui sind, wohin globalize.now schreibt, oder react-i18next, macht dabei keinen Unterschied. Kein Format bietet Platz für „und die Sidebar wandert auf die andere Seite“.
Zwei Dinge müssen sich ändern. Das Attribut dir am html-Element sagt dem Browser, in welche Richtung die Inline-Achse verläuft. Dein CSS muss aufhören, Seiten zu benennen, und stattdessen Anfang und Ende dieser Achse benennen. Alles Weitere folgt aus diesen beiden Punkten.
Was geht konkret kaputt, wenn du Arabisch zu einer Lovable-App hinzufügst?
Die Fehler häufen sich an vorhersehbaren Stellen, und sie betreffen alle das Styling, nicht die Texte.
- Navigations-Drawer und Sidebars bleiben physisch links.
- Chevrons, Zurück-Pfeile und Fortschrittsanzeigen zeigen in die falsche Richtung.
- Asymmetrisches Padding heftet Inhalte an den falschen Rand, sodass Labels sich auf einer Seite stauen.
text-leftauf einer Überschrift hält Arabisch weiterhin links im Container fest.- Absolut positionierte Badges und Schließen-Buttons landen in der entgegengesetzten Ecke, als eine Leserin es erwarten würde.
- Linke Rahmenlinien, die ein aktives Listenelement markieren, erscheinen auf der nachlaufenden statt der führenden Kante.
- Ein englischer Produktcode innerhalb eines arabischen Satzes ordnet sich ohne Richtungshinweis unvorhersehbar um.
Keiner dieser Fehler ist ein Übersetzungsproblem. Jeder Einzelne ist eine physische Richtung, die fest in einen Klassennamen eingebacken wurde.
Wie leite ich die Textrichtung aus dem Locale-Tag ab?
Frag das Locale, welche Richtung es verwendet, und schreib die Antwort ins Dokument.
Intl.Locale.prototype.getTextInfo() liefert für ein Locale-Tag entweder ltr oder rtl als Richtung zurück. Es erreichte im Juli 2026 den Status „Baseline newly available“, was bedeutet, dass es in der aktuellen Version jedes wichtigen Browsers gelandet ist – aber eben nicht in den meisten Browsern, die deine Nutzer tatsächlich einsetzen. Behandle den Fallback unten als den aktiven Weg und die API als das, was ihn irgendwann ablösen wird.
// getTextInfo() is not yet in TypeScript's bundled Intl.Locale definitions.
declare global {
namespace Intl {
interface Locale {
getTextInfo?(): { direction: "ltr" | "rtl" };
}
}
}
// Stopgap for runtimes without getTextInfo(). This set is the one place where
// adding a new right-to-left language still costs you a code change.
const RTL_FALLBACK = new Set([
"ar", "he", "fa", "ur", "ps", "sd", "yi", "dv", "ckb", "ug", "nqo", "syr",
]);
export function directionFor(locale: string): "ltr" | "rtl" {
let tag: Intl.Locale | undefined;
try {
tag = new Intl.Locale(locale);
} catch {
// Malformed tag. Fall through to the string path below.
}
const direction = tag?.getTextInfo?.().direction;
if (direction === "ltr" || direction === "rtl") return direction;
const language = tag?.language ?? locale.split(/[-_]/)[0].toLowerCase();
return RTL_FALLBACK.has(language) ? "rtl" : "ltr";
}
Die beiden try-Fälle sind entscheidend. new Intl.Locale() wirft bei einem fehlerhaften Tag einen RangeError, und getTextInfo() fehlt auf älteren Runtimes schlicht. Nur den zweiten Fall zu behandeln, ist der übliche Bug: Ein Locale wie ar_EG, das über einen Query-String hereinkommt, schickt die Funktion in ihren eigenen Recovery-Pfad und direkt wieder heraus – als nicht abgefangener Fehler.
Setze beide Attribute überall dort, wo deine App das aktive Locale bereits kennt. Lovable erzeugt derzeit zwei Grundformen, eine Vite-Single-Page-App und eine serverseitig gerenderte TanStack-Start-App – prüf also, was dein Projekt tatsächlich generiert hat. Bei TanStack Start ist es die Root-Dokumentkomponente. Bei Vite ist es die Stelle, an der du den i18n-Provider einbindest.
<html lang={locale} dir={directionFor(locale)}>
Setze dir auf html statt auf einen umschließenden div. Portale, Modals und Toasts rendern außerhalb deines Komponentenbaums und müssen das Attribut trotzdem erben.
Welche Tailwind-Klassen musst du für Rechts-nach-links ändern?
Diejenigen, die eine physische Seite benennen. Ihre logischen Entsprechungen folgen der Inline-Achse und drehen sich selbst um, wenn sich dir ändert.
| Physische Utility | Logische Entsprechung | Was sie festlegt |
|---|---|---|
| ml-4 / mr-4 | ms-4 / me-4 | margin-inline-start / margin-inline-end |
| pl-6 / pr-6 | ps-6 / pe-6 | padding-inline-start / padding-inline-end |
| left-0 / right-0 | start-0 / end-0 | inset-inline-start / inset-inline-end |
| text-left / text-right | text-start / text-end | Textausrichtung auf der Inline-Achse |
| border-l / border-r | border-s / border-e | führende / nachlaufende Rahmenlinie |
| rounded-l-lg / rounded-r-lg | rounded-s-lg / rounded-e-lg | führende / nachlaufende Ecken |
| float-left / float-right | float-start / float-end | Float auf der Inline-Achse |
| clear-left / clear-right | clear-start / clear-end | clear auf der Inline-Achse |
| scroll-ml-4 / scroll-mr-4 | scroll-ms-4 / scroll-me-4 | Scroll-Snap-Margin |
Tailwinds Doku zu Margins zeigt den Effekt direkt: ms-8 und me-8, gerendert innerhalb von dir="ltr"- und dir="rtl"-Containern, sitzen auf gegenüberliegenden Seiten. Utility-Klassen mit logischen Properties kamen erst mit Tailwind v3.3 — ein länger laufendes Projekt hat sie unter Umständen noch nicht.
Zwei Utility-Klassen brauchen eher einen zweiten Blick als einen neuen Namen. space-x-* und divide-x-* fügen Abstand und Rahmen zwischen Geschwister-Elementen ein — in einer umgedrehten Zeile landen sie deshalb auf der falschen Seite jedes Kindelements. Tailwind liefert dafür genau space-x-reverse und divide-x-reverse. Vertikale Utility-Klassen wie mt-* und pb-* brauchen nichts davon, da die Richtung nur die Inline-Achse betrifft.
Was können logische Properties nicht lösen?
Alles, was eine Richtung außerhalb des Box-Modells kodiert — genau dort haben die rtl:- und ltr:-Varianten ihre Berechtigung.
<ChevronRight className="rtl:rotate-180" />
<div className="bg-[url(/hero.svg)] bg-left rtl:bg-right" />
<div className="shadow-[4px_0_8px_rgba(0,0,0,.15)] rtl:shadow-[-4px_0_8px_rgba(0,0,0,.15)]" />
Transforms, Hintergrundpositionen, Schatten-Offsets, Gradient-Winkel, Carousel-Scroll-Mathematik und jedes Icon, das „vorwärts“ bedeutet, gehören hierher. Die Liste kurz halten. Wenn rtl: bei jedem zweiten Element auftaucht, sind die zugrunde liegenden Styles noch physisch und sollten stattdessen konvertiert werden.
Nutzergenerierte Inhalte brauchen eine eigene Behandlung, und die beiden Fälle unterscheiden sich. Für einen ganzen Block, dessen Sprache du nicht kontrollierst, lässt dir="auto" den Browser die Grundrichtung des Blocks anhand des ersten starken Zeichens bestimmen.
<p dir="auto">{review.body}</p>
Für einen fremdsprachigen Textabschnitt innerhalb eines Satzes mit bereits festgelegter Richtung — etwa eine englische SKU in arabischem Fließtext — bewirkt dir="auto" nichts, weil es die Grundrichtung des Absatzes setzt und keinen Inline-Abschnitt isoliert. Diesen Abschnitt stattdessen umschließen.
<bdi>{product.sku}</bdi>
Warum kann ein Übersetzungs-Widget dein Layout nicht einfach für dich umdrehen?
Weil ein Widget auf Textknoten in der gerenderten Seite operiert, dein Richtungsproblem aber in den Styles steckt, die diese Seite erzeugt haben.
Ein Runtime-Service kann dir="rtl" ins Dokument einschleusen und sichtbare Strings übersetzen, nachdem die englische Version bereits gerendert wurde. Er kann nicht deine Komponenten öffnen und ml-4 in ms-4 verwandeln, weil ml-4 bereits zu margin-left in einem Stylesheet kompiliert wurde, das der Service nicht besitzt. Das Ergebnis: arabischer Text in linksbündigem Layout-Mobiliar.
Es gibt noch einen zweiten Preis. Lovalingo, ein Runtime-Übersetzungsdienst, der bei manchen Lovable-Apps im Einsatz ist, hat angekündigt, am 31. August 2026 um 23:59 MESZ den Betrieb einzustellen. Die Migrationshinweise raten Kunden, auf projekteigene Internationalisierung umzusteigen und Locale-Routen, kanonische URLs und hreflang vor dem Entfernen erneut zu prüfen. Layout-Arbeit, die innerhalb des Produkts eines Anbieters erledigt wird, geht mit dem Anbieter verloren. Logische Utility-Klassen und ein dir-Attribut im eigenen Repository nicht. Unser Lovalingo-Vergleich führt das gleiche Argument für die String-Ebene an.
Wie verhinderst du, dass der nächste Lovable-Prompt das wieder zunichtemacht?
Einmal konvertieren, dann die physischen Utility-Klassen im Review sichtbar machen.
- Den Workflow einrichten. Im Lovable-Workspace den Skill
lovable-i18nhinzufügen und Lovable per Prompt anweisen, i18n einzurichten. Außerhalb des Editors erfolgt die Installation übernpx skills add globalize-now/globalize-skills, mit--allzur Installation für jeden Agent. - Die Richtung anhand der Locale setzen, mit dem oben genannten
directionFor, am Elementhtml. - Die physischen Utility-Klassen in deinen Komponenten auf logische umstellen — beginnend mit dem, was deine Seiten umschließt.
rtl:-Varianten nur für Transforms, Hintergründe, Schatten und richtungsabhängige Icons hinzufügen, und für fremdsprachige Inline-Abschnitte zu<bdi>greifen.- Einen Grep-Check ins Review einbauen, damit eine neu generierte physische Utility-Klasse schon vor dem Merge durchfällt.
rg -n '\b(ml|mr|pl|pr|scroll-m[lr])-|\b(float|clear|text)-(left|right)\b|\bborder-[lr]\b|\brounded-[lr]-|\b(left|right)-[0-9]' src/
Schritt fünf ist der, der wirklich trägt. Lovable generiert Komponenten bei jedem Prompt neu, und ein Modell, das gebeten wird, ein Einstellungspanel hinzuzufügen, greift zu ml-4, weil physische Utility-Klassen den React-Code dominieren, mit dem diese Modelle trainiert wurden. Ein Check, der den Pull Request durchfallen lässt, ist billiger, als das Problem drei Wochen später in einem arabischen Screenshot zu entdecken.
Die String-Hälfte des Kreislaufs läuft von selbst. globalize.now extrahiert neue hartcodierte Strings aus allem, was Lovable generiert hat, schreibt sie in deine Kataloge und synchronisiert bei jedem Git-Push, sodass der arabische Build aktuell bleibt, während du weiter promptest. Das ist derselbe Mechanismus, der im Vite-Guide und im TanStack-Start-Guide beschrieben wird — und der Grund, warum Übersetzungen nicht mehr bei jedem Push kaputtgehen.
Arabisch hinzuzufügen kostet auf dem Plan nichts extra. Der Starter-Plan liegt bei 20 € pro Monat und Workspace, inklusive 20 € Übersetzungs-Guthaben (rund 200.000 Wörter), und die Nutzung läuft danach zum gleichen Preis pro Zeichen weiter — kein Aufpreis pro Sitz und kein Aufpreis pro Sprache, RTL-Sprachen inklusive. Mehr zum Setup findest du auf der Lovable-Integrationsseite, technische Details auf der Entwickler-Seite, und einen kürzeren Weg für nicht-technische Builder auf der Vibe-Coder-Seite.
Eine Layout-Änderung, und sie bleibt fix
Richtung ist eine Entscheidung, die dein Repository einmal trifft. Die Strings sind der Teil, der sich bei jedem Prompt ändert — und der läuft von selbst.
globalize.now konvertiert hartcodierte App-Texte in übersetzungsreife Locale-Dateien und hält sie aktuell, während Sie veröffentlichen.
Probiere globalize.now kostenlos