In a component library, why should elements that display amounts and dates use locale-aware formatter hooks instead of formatting values themselves?
answer
- the element does not know the locale
- separators, symbol position, date order
- locale formats, the data says which currency
- one formatter supplied at the root
- parse with the same rules you display
basics
~20 sFormatting 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.
solid answer
~50 sThe same amount is written `1,234.56` in one locale and `1.234,56` in another, with the currency symbol before or after it; dates change order and sometimes calendar. An element that formats values with its own fixed pattern gives every consumer one country's format. So the element takes **raw values** — a number and a currency code, a date — and formats them through a **formatter hook**: a function or locale context the app supplies at its root, usually built on the platform's locale-aware formatting services. Two details matter. The **currency comes from the data**, not the locale: the locale decides how an amount is written, the bill decides which currency it is. And an element that accepts typed amounts must **parse** with the same locale rules it displays with, or "1.234" is read as a thousand in one locale and as one in another.
code
pseudocode · 18 lines// provided once by the app at its root
localeContext = {
locale: "de-CH",
formatAmount(value, currencyCode) -> text,
formatDate(date, style) -> text,
parseAmount(text) -> value or error
}
// inside the amount-due element
inputs: value, currencyCode // currency comes from the bill data
text = localeContext.formatAmount(value, currencyCode)
render text
// inside the partial-payment input element
on text entered:
result = localeContext.parseAmount(text)
if result is error: show format hint, keep previous value
else: store result as the raw valuego deeper
Recall that separators, currency placement and date order differ by locale, so elements should not format values with one fixed pattern.
Explain the hook contract: raw values in, a formatter supplied by the app at its root, a library default, and why currency travels with the data rather than the locale.
Cover the input side and the edge cases: parsing with the display locale, ambiguous input, keeping raw values, and keeping rounding and conversion out of elements.
Weigh raw-value hooks against preformatted strings as a library contract, and show how hooks turn a new market into configuration rather than a library-wide search.
## What varies by locale A **locale** is the combination of language and region that decides how values are written. For a utility company's billing portal, the same bill needs different renderings: | Value | One locale | Another locale | |---|---|---| | Amount | €1,234.56 | 1.234,56 € | | Date | 03/04/2026 meaning March 4 | 03.04.2026 meaning 3 April | | Meter reading | 12,345.6 kWh | 12 345,6 kWh | | Digits | 0-9 | Some locales use their own digit shapes | The decimal and grouping separators, the currency symbol's position and spacing, the date order, the calendar and even the digit shapes change. A **component library** element that formats values itself, with one fixed pattern, gives every customer the format of whoever wrote the element. ## The formatter hook A **formatter hook** separates the value from its presentation: 1. The element accepts **raw values**: a number plus a currency code, a date, a quantity plus a unit. 2. The **app supplies formatting** once, at its root, through a locale context or formatter functions. 3. The element calls the formatter to produce display text and never builds the format itself. 4. The library can ship a **default formatter** built on the platform's locale-aware formatting services, used when the app provides none. This keeps one product consistent: the bill card, the payment history table and the usage chart all format through the same function, so they cannot disagree. An alternative contract is for elements to accept **preformatted strings**. It is simpler, but it pushes formatting into every call site, loses the raw value the element might need for alignment, comparison or charting, and makes inconsistencies more likely. ## Locale and currency are different things - The **locale** decides *how* an amount is written: separators, symbol position, spacing. - The **currency** is a property of the *amount*, carried in the data with the value. A German-speaking customer with a bill in Swiss francs sees francs written in their locale's style; the interface language does not turn the bill into euros. An element that derives the currency from the locale shows the wrong money. ## Input: parse with the same rules Elements that accept values — an amount field for a partial payment, a meter-reading input — have the reverse problem. "1.234" means one thousand two hundred thirty-four in a locale that groups with dots, and just over one in a locale that uses the dot as a decimal separator. So: - **Parse with the same locale** used for display. - **Show the expected format** in the field's hint or example. - **Keep the raw value** in the element's state and format only for display. - **Never guess silently** when input is ambiguous; ask the user to correct it. ## Where the hook lives in the library - **One provider at the root** supplies the locale and the formatters; elements read it rather than each taking a locale option. - **A sub-tree may override it**, for example a statement preview rendered in the customer's billing locale inside an agent tool that uses the agent's own locale. - **Elements document which values they format**, and through which formatter, so the app knows what to supply. - **The default formatter is replaceable**, so a product with its own rules — say, always showing the currency code rather than a symbol on business accounts — changes it once. ## What the element must not do - **Round or convert** values: rounding rules and currency conversion are business logic, owned by the app or the back end. - **Hardcode units**: a meter reading's unit comes with the value. - **Read the device locale on its own**: the app decides the locale, often from the user's account preference, and elements must follow it so the page is consistent. ## The payoff With formatter hooks, adding a market is configuration rather than a change to every element. The billing portal launching in a new country supplies a locale, and every amount, date and reading in every element follows. Without them, the launch becomes a search through the library for hard-coded patterns, which is exactly the retrofit a localizable library is designed to avoid.
- When is accepting preformatted strings a reasonable contract for an element?For display-only elements where the value is never compared, aligned, sorted or charted, and where the app already formats consistently in one place. It keeps the element simple. It becomes a problem when the element needs the raw number, for example to align decimals in a table column or draw a chart, or when many call sites each format differently.
- Why should the app, not the element, decide the locale used for formatting?Because the locale is a product decision: it may come from the user's account preference, the market the site serves, or an explicit language switch. If each element read the device setting on its own, one page could mix formats. A locale supplied once at the root makes every element agree with the rest of the page.
saying these in an interview costs you the question
- The interface language determines which currency an amount is shown in.
- One fixed amount pattern is fine if it uses the most common separators.
- Parsing typed amounts is locale-independent once the digits are entered.
- Each element should read the device locale itself to stay self-contained.
- Elements should round and convert currency so display stays tidy.