Skip to main content

Localisation

Prism is authored to be robust to language and direction from the first line of CSS. Localisation is not a retrofit — it's a property of the layout primitives.

Direction (RTL)

The same component and the same code, left-to-right and right-to-left. Because Prism uses logical properties everywhere, order, icon placement, progress direction and focus all mirror automatically.

Left-to-right (dir="ltr")

Live demo
Open ↗

Right-to-left (dir="rtl")

Live demo
Open ↗

A form row mirrors the same way — labels, inputs and required markers all flip:

Live demo
Open ↗

The brand lockup also mirrors correctly — order flips, the mark itself does not:

Live demo
Open ↗

Logical properties

The whole UI mirrors under dir="rtl" for free, because Prism uses CSS logical properties everywhere — never physical ones:

UseNot
margin-inline-startmargin-left
padding-inlinepadding-left/right
inset-inline-startleft
border-inline-startborder-left
text-align: starttext-align: left

Set dir="rtl" on <html> (or any subtree) and layout, icons-with-text order, progress direction and focus order all mirror. Directional glyphs (chevrons, breadcrumb separators, back arrows) flip; non-directional icons don't. Test in QA with the RTL gate.

Long strings

German, Finnish and many others run far longer than English. Prism tolerates this by design:

  • Body line-height is a roomy 1.55; type sizes are rem so text-zoom composes.
  • Layout subtrees use the overflow/overlap guards in base.css: media caps at max-inline-size:100%; text wraps via overflow-wrap; add data-guard="layout" so flex/grid children may shrink below content size (the usual cause of a row overflowing); use .prism-truncate / .prism-clamp (with --lines) to clip deliberately rather than overflow.
  • Never size a control or container to fit one language's string. Let it grow or wrap.
  • Avoid text baked into images; keep labels in the DOM.

Locale-aware formatting

Numbers, dates, currency and lists are locale-aware, and right-aligned in tables with monospace (--font-mono) so digits line up across rows. Use the platform Intl APIs (React Aria's i18n hooks wrap these):

  • Intl.NumberFormat for numbers, percentages, currency (currency symbol, grouping and decimal separators vary by locale).
  • Intl.DateTimeFormat for dates/times — never hand-format. Prism's Calendar / DatePicker / DateRangePicker / TimePicker are built on React Aria's calendar system, which is locale- and calendar-system-aware (Gregorian and non-Gregorian) and respects first-day-of-week.
  • Intl.ListFormat / Intl.RelativeTimeFormat for conjunctions and "3 days ago".

Pseudo-localisation

Before a real translation exists, test with pseudo-localisation — accented, expanded, bracketed strings (e.g. [!!! Ŝéñð rémîñðér !!!]) — to surface truncation, concatenation and hard-coded-string bugs early. It's a QA gate (QA); keep fixtures in tests/pseudo-localisation/.

Authoring rules

  • Logical properties only. A physical left/right/margin-left in a component is a bug.
  • Never concatenate translated fragments into a sentence — word order varies. Use whole strings with named placeholders.
  • Don't hard-code formats, separators, or currency symbols — go through Intl.
  • Mirror-test every new component under dir="rtl" and expansion-test under pseudo-localisation.
  • Keep the measure readable (--measure, ~68ch) in every language for longform.