Direction
Right-to-left languages - who sets the direction, and how the components and formats follow it.
@workspace/active-i18n 1.1.0Developmentunreleased, in development in Foundationbaseline ce081dc3
Language, formats and reading directionClient
الطلب 1042
- تاريخ الطلب
- 21/10/2026، 11:30 ص
- المجموع
- 1,234.50 €
// apps/docs/examples/i18n/direction.tsx'use client';
import {useState} from 'react';
import {toIanaTimeZone} from '@workspace/active-format';import type {LanguageConfig, LocaleTranslations} from '@workspace/active-i18n';import { fixedTimeZone, I18nProvider, TimeZoneProvider, useFormatters, useLocale, useTranslations,} from '@workspace/active-i18n/react';import {DirectionProvider} from '@workspace/active-ui/components/primitives/direction';import {Slider} from '@workspace/active-ui/components/primitives/slider';import { Tabs, TabsList, TabsTrigger,} from '@workspace/active-ui/components/primitives/tabs';
// The host's language set: the direction of each language is the host's data.const LANGUAGES: readonly LanguageConfig[] = [ {code: 'en', label: 'English', direction: 'ltr'}, {code: 'ar', label: 'العربية', direction: 'rtl'},];
const TRANSLATIONS: Record<string, LocaleTranslations> = { en: { order: { title: 'Order {{id}}', placed: 'Placed on', total: 'Total', progress: 'Progress', }, }, ar: { order: { title: 'الطلب {{id}}', placed: 'تاريخ الطلب', total: 'المجموع', progress: 'التقدم', }, },};
function OrderSummary() { const t = useTranslations(); const locale = useLocale(); const format = useFormatters(); return ( <div className="flex flex-col gap-3"> <p className="font-medium" data-i18n-locale={locale}> {t('order.title', {id: '1042'})} </p> <dl className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1 text-sm"> <dt className="text-muted-foreground">{t('order.placed')}</dt> <dd>{format.dateTime('2026-10-21T09:30:00Z')}</dd> <dt className="text-muted-foreground">{t('order.total')}</dt> <dd>{format.currency({amount: 1234.5, currency: 'EUR'})}</dd> </dl> <Slider defaultValue={30} aria-label={t('order.progress')} /> </div> );}
/** * The language decides the words and the formats; the host decides the * direction from its language data and sets `dir` and `DirectionProvider`, * so the layout and the arrow keys follow the reading direction. */export default function Direction() { const [code, setCode] = useState('ar'); const language = LANGUAGES.find((entry) => entry.code === code)!; const direction = language.direction ?? 'ltr'; return ( <div className="flex w-full max-w-md flex-col gap-4"> <Tabs value={code} onValueChange={(value) => setCode(String(value))}> <TabsList aria-label="Language"> {LANGUAGES.map((entry) => ( <TabsTrigger key={entry.code} value={entry.code}> {entry.label} </TabsTrigger> ))} </TabsList> </Tabs> {/* A real host navigates to the other language (setLocale); this demonstration remounts the provider instead. */} <I18nProvider key={code} locale={code} translations={TRANSLATIONS[code]!} supportedLanguages={LANGUAGES} allowLocaleSwitching={false} > <TimeZoneProvider value={fixedTimeZone(toIanaTimeZone('Europe/Amsterdam'))} > <DirectionProvider direction={direction}> <div lang={code} dir={direction} className="rounded-lg border p-4" data-i18n-direction={direction} > <OrderSummary /> </div> </DirectionProvider> </TimeZoneProvider> </I18nProvider> </div> );}Who decides the direction#
The direction of a language is your data: LanguageConfig.direction ('ltr' or 'rtl')
carries it with your language set. The package does not set dir anywhere. Your layout does, on
<html> for a whole page (or on the region that shows another language), together with active-ui's
DirectionProvider, so that components whose keys depend on direction (tabs, sliders, menus) turn
around too:
import {DirectionProvider} from '@workspace/active-ui/components/primitives/direction';
const direction = language.direction ?? 'ltr';
<html lang={language.code} dir={direction}> <body> <DirectionProvider direction={direction}>{children}</DirectionProvider> </body></html>;What follows the language#
- Words come from your dictionary for that language.
- Formats come from the presentation:
useFormatters()formats dates, numbers and amounts for the provider's language, with the date order, separators and digits the runtime's CLDR data gives that language. - Layout follows
dir: logical CSS properties (margin-inline-start,ps-*) turn around; physical ones (left) do not.
A grid and a chart take their direction from their own dir prop or the page, never from the
locale, so the same rule holds there.