RTL language support without application rework
Right-to-left language support is one of those features that looks simple until you start implementing it. The translations themselves are the easy part. The hard part is that RTL languages like Arabic, Hebrew, and Persian require the entire page layout to mirror: text alignment reverses, flex directions flip, icons that point left on LTR should point right on RTL, and margins that should be on the trailing edge become leading-edge margins. Doing this correctly requires the application to know the direction of the active locale.
Messagevisor carries direction metadata on locale definitions and surfaces it through SDK methods and React hooks. Your application can query the current locale's direction and apply it to layout without building its own locale-to-direction lookup table.
Defining direction on locales#
Every locale definition can carry a direction field:
description: English basedirection: ltrformats: number: money: style: currency currency: USDdescription: Arabic (Saudi Arabia)direction: rtlformats: number: money: style: currency currency: SAR currencyDisplay: symboldescription: Hebrew (Israel)direction: rtlformats: number: money: style: currency currency: ILS currencyDisplay: symboldescription: Persian (Iran)direction: rtlformats: number: money: style: currency currency: IRRThe direction value is included in the built datafile. The SDK exposes it. Your application never needs a hardcoded mapping from locale codes to direction values.
Adding translated messages for RTL locales#
Translation authoring for RTL locales follows the same pattern as any other locale. The only difference is the characters in the translation strings:
description: Sign in button labeltranslations: en: Sign in ar-SA: تسجيل الدخول he-IL: התחבר nl: Aanmeldendescription: Home navigation labeltranslations: en: Home ar-SA: الرئيسية he-IL: ביתNo special handling is needed in the message definition for RTL. The content is just text. Direction is handled at the locale level and surfaced by the SDK.
Locale inheritance for RTL regional variants#
RTL locales can inherit translations from other RTL locales. If you have a base ar locale and a regional ar-SA variant, use inheritTranslationsFrom to avoid re-authoring all strings:
description: Arabic basedirection: rtlformats: number: decimal: maximumFractionDigits: 2description: Arabic (Saudi Arabia)direction: rtlinheritTranslationsFrom: arinheritFormatsFrom: arformats: number: money: style: currency currency: SARdescription: Arabic (United Arab Emirates)direction: rtlinheritTranslationsFrom: arinheritFormatsFrom: arformats: number: money: style: currency currency: AEDRegional Arabic variants inherit the base Arabic translations and only override the currency format. Translation authoring stays in one place.
Reading direction in JavaScript#
The JavaScript SDK exposes getDirection(locale?):
import { createMessagevisor } from "@messagevisor/sdk";import datafile from "./datafiles/messagevisor-web-ar-SA.json";const m = createMessagevisor({ datafile, locale: "ar-SA", context: { platform: "web" },});const direction = m.getDirection(); // "rtl"const englishDirection = m.getDirection("en"); // "ltr"// Apply to document rootdocument.documentElement.setAttribute("dir", direction);document.documentElement.setAttribute("lang", m.getLocale());If you are passing locale explicitly, getDirection(locale) looks up the direction for that locale in the loaded datafile.
React hooks for direction-aware layout#
The React SDK provides useDirection() and useLocaleInfo() for direction-aware rendering:
import { useDirection, useLocaleInfo } from "@messagevisor/react";function AppShell({ children }: { children: React.ReactNode }) { const direction = useDirection(); const { locale } = useLocaleInfo(); return ( <div dir={direction} lang={locale}> {children} </div> );}useDirection() is reactive: when the active locale changes (including locale switches at runtime), the hook re-renders with the new direction value. If a user switches from English to Arabic in a locale picker, your layout re-mirrors automatically.
Conditional class names based on direction#
Many RTL-aware CSS patterns involve toggling classes or CSS custom properties based on direction:
import { useDirection } from "@messagevisor/react";function NavigationPanel() { const direction = useDirection(); const isRTL = direction === "rtl"; return ( <nav className={`navigation ${isRTL ? "navigation--rtl" : "navigation--ltr"}`} > {/* navigation items */} </nav> );}Using both locale and direction together#
import { useLocaleInfo, useTranslation } from "@messagevisor/react";function PageHeader() { const { locale, direction } = useLocaleInfo(); const homeLabel = useTranslation("nav.home"); return ( <header dir={direction} lang={locale}> <a href="/">{homeLabel}</a> </header> );}Locale switching at runtime#
The SDK and React package handle locale switches gracefully. When the active locale changes to an RTL locale, all reactive hooks update:
import { useMessagevisor, useDirection } from "@messagevisor/react";function LocalePicker() { const { setLocale } = useMessagevisor(); const direction = useDirection(); return ( <div> <span>Current direction: {direction}</span> <button onClick={() => setLocale("en")}>English</button> <button onClick={() => setLocale("ar-SA")}>عربي</button> <button onClick={() => setLocale("he-IL")}>עברית</button> </div> );}After calling setLocale("ar-SA"), useDirection() returns "rtl" and any component using it re-renders.
The catalog renders RTL content correctly#
The catalog understands locale direction metadata and uses it when rendering translation cells and evaluated example output. RTL strings are rendered with appropriate text direction so they are readable during review. Mixed-script content - Arabic body text with Latin product names, URLs, or numbers embedded - is rendered with the appropriate dir context.
$ npx messagevisor catalogThis is particularly useful when reviewing translations before publishing: reviewers can see RTL content rendered correctly in the catalog without needing to load the full application.
Testing direction metadata#
Assert that direction is correctly configured on each locale:
locale: ar-SAassertions: - description: SAR currency format expectedFormats: number: money: currency: SARDirection is locale metadata rather than a format value, so it does not appear as an expectedFormats assertion. The catalog and getDirection() surface it at runtime. If you need to verify it programmatically, inspect the built datafile:
$ npx messagevisor build --target=web --locale=ar-SA$ cat datafiles/messagevisor-web-ar-SA.json | jq '.direction'Important nuance: direction is metadata, not layout#
Messagevisor surfaces direction information. Your application decides how and where to apply it. Setting dir="rtl" on the document root is usually the right starting point for a full locale switch, but more granular layout behavior - mirroring icons, flipping flex directions, adjusting padding - is your application's responsibility.
Think of getDirection() and useDirection() as the data source. Your CSS, component library, and layout system are the mechanism. This separation means Messagevisor stays focused on translation delivery while you retain full control over how direction affects your UI.

