Skip to main contentSkip to docs navigation

Internationalization

How react-aria's I18nProvider drives locale-aware behavior in the date and menu components, and how that's separate from chassis-css's own dir-attribute RTL support.

A handful of components read the active locale to decide segment order, weekday names, the calendar system, weekend styling, or which direction a submenu cascades toward. DatePicker, DateRangePicker, Calendar, RangeCalendar, and MenuSubmenu all read that locale the same way — from react-aria's I18nProvider, falling back to the browser's own locale when none is set. This page covers setting that locale and its effect, and how it relates to — but doesn't replace — chassis-css's separate dir-attribute RTL support.

Setting a locale

Wrap an application, or any subtree, in I18nProvider with a BCP47 language tag:

TSX
import { DatePicker, I18nProvider } from '@chassis-ui/react'

<I18nProvider locale="ar-SA">
  <DatePicker label="Departure date" />
</I18nProvider>

Every component listed above reads whichever I18nProvider is nearest up the tree, the same way React context normally resolves — a locale set closer to a specific component overrides one set higher up. With no I18nProvider in the tree at all, these components fall back to the browser's own locale.

Setting ar-SA above changes more than translated strings: DatePicker's segment order and weekday names follow the locale, its calendar system switches from Gregorian to Umm al-Qura, and Calendar/RangeCalendar shift which days render as the muted "weekend" style. None of this is configured through a prop on the date components themselves — it all flows from the locale.

Import I18nProvider from @chassis-ui/react, not from react-aria directly. react-aria is bundled into this package rather than installed as a peer, so its module — including the context I18nProvider provides — is a separate copy from any react-aria a consuming application installs on its own. An I18nProvider imported from react-aria sets a different context instance and silently has no effect on this library's components.

Right-to-left

Two independent mechanisms contribute to RTL support, and setting one doesn't set the other.

I18nProvider's locale determines a react-aria direction ('ltr' or 'rtl') that MenuSubmenu reads via useLocale() — an RTL locale flips which side a submenu cascades toward and which arrow key opens versus closes it, matching native OS/browser submenu behavior. This mechanism is scoped to whichever components actually call useLocale() internally; it doesn't touch anything else on the page.

Everything else — layout mirroring, icon direction, chassis-css's own components — follows the dir attribute instead, set on <html> or a scoping ancestor, independently of I18nProvider. Pagination's Prev/Next chevrons, for example, mirror under an ancestor dir="rtl" through a plain CSS rule, with no react-aria involvement at all. See chassis-css's own RTL page for the full logical-properties story this covers.

A fully consistent RTL experience needs both set together — an RTL locale with no dir="rtl" gets a correctly-mirrored submenu, an Umm al-Qura calendar, but LTR-oriented icons and layout everywhere else:

TSX
<html lang="ar" dir="rtl">
  <I18nProvider locale="ar-SA">
    <App />
  </I18nProvider>
</html>

Date values

Calendar, RangeCalendar, DatePicker, and DateRangePicker accept date values as @internationalized/date objects rather than Date instances or ISO strings — install it separately to construct them:

Shell
npm install @internationalized/date
TSX
import { Calendar } from '@chassis-ui/react'
import { today, getLocalTimeZone } from '@internationalized/date'

<Calendar value={today(getLocalTimeZone())} />

Unlike I18nProvider, there's no module-identity concern here — a CalendarDate is a plain data object, not something read through React context, so a separately-installed copy of @internationalized/date works the same as any other dependency. See Calendar and DatePicker for the full range of value/selection props.

Labels outside the locale system

Not every user-facing string in these components comes from the active locale. react-aria ships translations for the strings it owns, but a handful of strings these components render themselves have no equivalent in those dictionaries. They default to English and are translated with a plain prop, the same way any other label prop would be — I18nProvider has no effect on them.

Pagination's Prev/Next controls take one prop each:

TSX
<Pagination pages={10} activePage={page} onActivePageChange={setPage} previousLabel="Précédent" nextLabel="Suivant" />

Breadcrumb names its wrapping <nav> landmark "breadcrumb" by default — override it with aria-label, which is also how you tell two breadcrumb trails on one page apart:

TSX
<Breadcrumb aria-label="Fil d'Ariane" items={items} />

The calendar family — Calendar, RangeCalendar, DatePicker and DateRangePicker — groups its own strings under a single labels prop, because several of them are rendered by internal parts a consumer never mounts directly (the year view's paging arrows, its live-region announcements, the month/year header buttons, the clear adornment):

TSX
<DatePicker
  label="Date de départ"
  labels={{
    calendar: 'Calendrier',
    clear: 'Effacer',
    month: 'Mois',
    year: 'Année',
    selectMonth: 'Choisir le mois',
    selectYear: "Choisir l'année",
    previousYears: 'Années précédentes',
    nextYears: 'Années suivantes'
  }}
/>

labels is merged over the English defaults, so passing one key leaves the rest alone. A DatePicker's labels also reaches the Calendar it renders inside its own popover — you don't set it twice. The CalendarLabels type is exported if you want to define one shared object per locale:

TSX
import type { CalendarLabels } from '@chassis-ui/react'

const fr: Partial<CalendarLabels> = { calendar: 'Calendrier', clear: 'Effacer' }

Everything else these components say — month and weekday names, segment order, the calendar system — still follows I18nProvider's locale and needs no override.