skip to content

Right-to-Left & Locale Hooks

Making library elements localizable: right-to-left mirroring, room for text expansion, locale-aware formatting hooks, labels passed in not hardcoded. Retrofitting any of these later is costly.

part ofDesign systems & UX foundationsoverview, primer and where to startread it →
on this pageshow

questions

5

In a component library, why should an element's visible text arrive as translatable inputs instead of being hardcoded inside the element?

level: juniorimportance: must knowfreq 45%

answer

  1. the library cannot know every language
  2. the app owns translation
  3. whole messages, not joined fragments
  4. named placeholders translators can move
  5. defaults for internal strings, overridable

basics

~20 s

A library cannot know which languages its products ship in, so hardcoded text forces one language on every consumer. Elements should take complete, translatable messages from the app, which owns translation, and let every internal default string be overridden.

solid answer

~40 s

Text baked into an element ships one language to every product and can only change with a library release. So visible text — button labels, headings, helper text, empty states — arrives as **inputs**, and the consuming app supplies it from its own translation system. Two rules make that work. First, pass **whole messages with named placeholders** ("Amount due {amount} by {date}"), never fragments the element joins in a fixed order, because word order differs between languages. Second, text the library owns internally — a pagination element's "Next page", a file picker's "Remove" — ships as **defaults that consumers can override**, ideally from one message bundle per locale, so an app can translate every string without forking an element. The same logic keeps text out of images and icons.

go deeper

for a junior

Recall the rule: visible text is passed in, not written inside the element, and messages are whole sentences with named placeholders rather than joined fragments.

for a middle

Explain how internal library strings work: defaults, one override point at the app's root, per-instance overrides, and why the app, not the library, owns translation.

for a senior

Spot the hidden localization defects in an existing element — concatenation, text in icons, element-added punctuation, casing assumptions — and plan how to fix them without breaking consumers.

for a principal

Argue for localization readiness as an early library decision, since retrofitting text inputs later touches every element, every call site and every test.

## The problem with text inside an element A **component library** is used by many products, and each product decides which languages it supports. A utility company's billing portal might launch in English and German, add Arabic and Hebrew next year, and serve a Finnish subsidiary through the same library. If the bill-summary card has the words "Amount due" written inside it, every product gets English, and changing that needs a library release. **Translation** — turning source text into each language — belongs to the app, which knows its languages, its translation workflow and its users' preferences. So the rule is simple: **visible text arrives as inputs**, and the element renders whatever it is given. ## What should be an input | Kind of text | Example in the billing portal | Who provides it | |---|---|---| | Action labels | "Pay now", "Download bill" | The app, per use | | Headings and body text | "Your electricity usage" | The app, per use | | Helper and error text | "Enter the reading shown on your meter" | The app, per use | | Empty and loading states | "No bills yet" | The app, per use | | Internal element strings | A pagination element's "Next page", a date picker's month names | Library default, overridable | ## Whole messages, not fragments The most common localization defect in a library is **string concatenation**: an element builds "Due" + date or "Page" + 3 + "of" + 12 in a fixed order. Translators cannot reorder fragments, and many languages put the date, the number or the verb somewhere else in the sentence. The fix: - Pass **one complete message** per piece of text. - Use **named placeholders** (`{amount}`, `{date}`) rather than positional joins, so translators can move them. - Let the app's message system handle **grammatical variation** such as plural forms; the element should receive the finished string. - Keep **formatting of values** (amounts, dates) separate from translation, so the app formats them for the locale before they fill the placeholders. ## Library-owned strings: defaults plus overrides Some text belongs to the element itself: the "Next page" and "Previous page" of a pagination element, the "Remove file" action of an upload element, the month and weekday names of a date picker. Requiring every consumer to pass every one of these is heavy, so libraries usually: 1. **Ship defaults** in the library's source language, or ship locale packs for the languages the library supports. 2. **Expose one override point**, typically a message bundle supplied at the app's root, so the app can translate every internal string at once. 3. **Allow per-instance overrides** for the rare case where one screen needs different wording. 4. **Document every internal string** with its key and meaning, so translators get context. ## What should stay out of an element - **Text inside images or icons**: a "PAID" stamp drawn into an icon cannot be translated. Use a text badge instead. - **Assumptions about letter case**: automatic capitalisation assumes the language has letter case; many scripts do not, and some languages have their own casing rules. - **Fixed word order in layout**: an element that places a label before a value because English does so may need a reversible order. - **Punctuation added by the element**: a colon after a label, or quotation marks around a name, differ by language and should be part of the message. ## Why retrofitting is costly When text is hardcoded, localizing later means changing every element's API, every consumer's call site and every test that matched the English text. When text is an input from day one, adding a language is a translation task, not an engineering project. That asymmetry is why localization readiness is a library-level decision made early, not a feature added when the first non-English market arrives.

  • Should a component library ship its own translations for internal strings?
    It can, and many do ship locale packs for their internal strings such as month names or pagination labels. That is a convenience, not a replacement for overrides: products support languages the library does not, and teams want their own terminology. The contract that matters is that every internal string can be replaced from the app without forking the element.
  • Why must the placeholders in a message be named rather than positional?
    Because a translator may need to reorder them. "Amount due {amount} by {date}" can become a sentence in another language where the date comes first. With named placeholders the translator moves them freely and the element still fills the right value into each; with positional joins the order is frozen by the element's code.

saying these in an interview costs you the question

  • Hardcoding English is fine because it can be translated later.
  • Building a label by joining fragments is fine if each fragment is translated.
  • Internal element strings are rarely seen, so they need no override.
  • A text stamp drawn into an icon counts as translatable content.
  • Translation is the library's job, since it owns the elements.
open as a page

In a component library, why should elements that display amounts and dates use locale-aware formatter hooks instead of formatting values themselves?

level: middleimportance: should knowfreq 38%

basics

~20 s

Formatting rules — separators, currency placement, date order, digits — differ by locale, and only the app knows the user's locale. Elements should take raw values and format them through a formatter the app supplies, so every element in a product agrees.

open as a page

When a component library's elements render in a right-to-left language, what should mirror, and which icons should not flip?

level: middleimportance: should knowfreq 40%

basics

~20 s

Everything that expresses reading direction mirrors: element order, start and end alignment, back and next arrows, chevrons, steppers and progress. Icons of real objects, clocks, logos and usually media controls stay unflipped, and numbers keep their left-to-right order.

open as a page

In a component library, how should elements handle text that grows after translation, and when is truncating it acceptable?

level: middleimportance: should knowfreq 45%

basics

~20 s

Elements should let text grow or wrap — no fixed widths or heights on text-bearing parts — because translations are often longer. Truncate only non-critical text in constrained spots, and keep the full text reachable; never truncate actions, amounts, dates or errors.

open as a page

A utility billing portal's component library used left and right spacing everywhere and must now launch in Arabic and Hebrew; how do you retrofit it for right-to-left?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Inventory every physical left/right use, convert spacing, alignment, radii, offsets and motion to logical start/end, take direction from the app's root, flag directional icons, then add a lint guard and both-direction checks so physical values cannot return.

open as a page