# Localization

## What it is

Localization is the work of adapting a product to one particular market: its
language, and with it the conventions its readers expect for dates, numbers,
prices, names, addresses and the order of a list. The W3C defines it as adapting
a product, an application or the content of a document to one target market, so
that it suits that market's language and culture and meets whatever else the
market needs, and it calls that market a locale. The word is also spelled
localisation, and it is shortened to l10n, for the 10 letters between its first
l and its last n.

The W3C adds that localization is often thought of as a synonym for translating
the interface and the documentation, and that it is often a substantially more
complex job. Its list of what may need adapting covers numeric, date and time
formats; currency; keyboard layout; collation and sorting; symbols, icons and
colours; text and graphics that a culture may misread or find insensitive; legal
requirements; the formats of addresses, postal codes, phone numbers and the
order of names; and the readability needs of each script. Where the way of doing
business differs, with accounting as the W3C's example, the adaptation may mean
rethinking the logic, the visual design or the presentation of the product.

Localization depends on internationalization and comes after it.
Internationalization is done once, in the code, so that nothing about a single
market is built into it. Localization is done for each locale, in data the code
reads: the translated messages, and the conventions that turn a stored date or
amount into the text a reader expects. A product that has been internationalized
well can gain a locale without a change to its code; one that has not must be
changed again for every market it enters.

Most of the convention data does not have to be written by the team. The Unicode
Consortium's CLDR project collects it for over 100 languages, checked by
specialists and people who speak each language natively, and its own example is
the list of files on a phone: the format of the dates, the numbers, the units of
the file sizes and the alphabetical order all vary with the language and are
usually supplied by CLDR. The project states that its data is built into all
modern operating systems and browsers and into languages such as Java, C#, Swift
and JavaScript. A product gets most of its formats from the platform, provided
the code asks the platform for them instead of writing them itself.

## Why it matters

A product that is translated but not localized reads as foreign. Every label is
in Danish, and the date still puts the month first, the price still carries a
dollar sign and the list of towns still files Aarhus under A. Readers notice
each of these at once, and each one tells them the product was made for somebody
else.

Some of the mismatches change what the reader understands. The string 10/11/2026
is 11 October to an American reader and 10 November to a British one; both are
valid dates, so nothing fails, and one of the two readers acts on the wrong day.
The string 1,234 is one thousand two hundred and thirty-four in English and one
point two three four in Danish, a factor of a thousand in a quantity or a price.
A wrong translation looks wrong. A wrong convention looks right and is read
wrongly.

The W3C's list also reaches past the screen. Legal requirements differ between
markets, and so does the way a business is run, so a locale can need different
terms, different invoice details or a different flow through a purchase. These
are decisions for the business, and they belong in the plan for a locale before
the first message is translated.

The cost is per locale and it does not end. Every new message has to be
translated into every language, every release has to be read in every locale
before it ships, and every market brings its own conventions to check. A team
that adds a language adds that work to each later release, which is why the
decision to enter a market is also a decision about the release process.

## What it looks like in practice

The platform does most of the work when the code passes it a value and a locale.
In JavaScript, the `Intl` formatters take both and return text:

```js
const when = new Date("2026-10-27T12:00:00Z");
const money = { style: "currency", currency: "DKK" };
for (const locale of ["en-US", "en-GB", "da", "nb", "sv", "uk"]) {
  const date = new Intl.DateTimeFormat(locale, { dateStyle: "short" });
  const price = new Intl.NumberFormat(locale, money);
  console.log(locale, date.format(when), price.format(800));
}
```

Run in Node.js 25, which carries data from CLDR 47, it prints one date and one
price of 800 Danish kroner six ways:

```text
en-US 10/27/26 DKK 800.00
en-GB 27/10/2026 DKK 800.00
da 27.10.2026 800,00 kr.
nb 27.10.2026 800,00 DKK
sv 2026-10-27 800,00 Dkr
uk 27.10.26 800,00 DKK
```

The code chose none of it. The Danish reader sees kr., the Swedish reader Dkr,
because kr in Sweden means the Swedish krona, and the Norwegian and Ukrainian
readers see the currency code. The order of day and month, the separators, the
decimal comma and the place of the currency all come from the locale data. In
the real output the space between the amount and its currency is a no-break
space, U+00A0, which keeps the two on one line.

Sorting works the same way. A list of six Danish place names, sorted for three
locales:

```js
const towns = ["Aabenraa", "Ærø", "Åkirkeby", "Odense", "Østerbro", "Vejle"];
for (const locale of ["en", "da", "sv"]) {
  const order = new Intl.Collator(locale).compare;
  console.log(locale, towns.toSorted(order).join(", "));
}
```

```text
en Aabenraa, Ærø, Åkirkeby, Odense, Østerbro, Vejle
da Odense, Vejle, Ærø, Østerbro, Aabenraa, Åkirkeby
sv Aabenraa, Odense, Vejle, Åkirkeby, Ærø, Østerbro
```

English treats æ, ø and å as forms of a and o. Danish places Æ, Ø and Å after Z
and files the old spelling Aa as Å, so Aabenraa moves from first to next to
last. Swedish puts its own Å, Ä and Ö after Z and files Æ with Ä and Ø with Ö.
Each order is correct for its readers, and a Danish reader who looks for
Aabenraa near the end of a list sorted for English has reason to think it is
missing.

A Hugo site has the same tools in its templates. Hugo's documentation passes one
amount through `lang.FormatCurrency`:

```text
{{ 512.5032 | lang.FormatCurrency 2 "USD" }}
```

An English page shows it as $512.50, a French one as 512,50 $US and a German one
as 512,50 $, and `lang.FormatNumber` and `lang.FormatPercent` do the same for
plain numbers and percentages.

Localization is part of one of this site's services. The
[internationalization and localization service](/services/i18n-localization/)
describes the aim as software that reads naturally in every market a client
sells in, including the dates, the currencies and the way names sort. Among its
results is a cross-language content check that fails when a translation drops a
figure the English states, or when a language uses notation it does not use, and
that found two Danish descriptions missing a metric and sixteen Ukrainian spans
quoting in the English style.

## Where it goes wrong

**Localization is budgeted as translation.** The plan for a new market covers
the message files and nothing else, so the product launches in the new language
with the old market's dates, prices, addresses and sort order, and the gaps are
found by customers. Plan each locale against the W3C's whole list, decide which
items apply to that market, and have a native reader review the built product
rather than the message files.

**A language is taken for a locale.** `en` does not say whether 27 October is
`10/27/26` or `27/10/2026`, and the output above shows that the two English
locales disagree on both the order and the year. A product that stores only the
language must guess the rest, and it guesses one market's conventions for every
other. Keep a full tag such as `en-GB` or `da-DK` for each user, take it from an
explicit setting where there is one, and pass it to every formatter.

**The currency is guessed from the locale.** CLDR's specification is blunt about
it: an amount should never be passed between systems without a known currency
code, or the same number 3.5 may end up "interpreted as $3.50 by one user and
€3.50 by another." It adds that the currency of an amount is independent of the
reader's language and that only badly designed software leaves the code out and
infers it from the locale. In Scandinavia the guess goes wrong quietly, because
Danish, Norwegian and Swedish each abbreviate their own currency kr, so a price
stored as 800 reads as 800 Danish kroner to one reader and 800 Norwegian kroner
to the next. Store every amount with its ISO 4217 code and format it for the
reader; a Danish reader then sees a Norwegian price as 800,00 NOK.

**Lists are sorted by code point.** JavaScript's default `sort` compares UTF-16
code units, which puts Є and І before the rest of the Ukrainian alphabet and Ґ
after Я, because their code points lie outside the range that runs from А to я.
Sorted that way, `["Ялта", "Ґанок", "Гора", "Єнот", "Іван", "Дуб"]` comes out as
Єнот, Іван, Гора, Дуб, Ялта, Ґанок; with `new Intl.Collator("uk").compare` it is
Гора, Ґанок, Дуб, Єнот, Іван, Ялта, the order of the Ukrainian alphabet. Sort
text for display with a collator for the reader's locale, in the code or in the
database's collation, and keep code-point order for keys that no reader sees.

**Tests compare formatted text typed by hand.** A test that expects `800,00 kr.`
with an ordinary space fails against the formatter, which puts U+00A0 there, and
the same no-break space separates the thousands in Norwegian, Swedish and
Ukrainian. Locale data is also revised from one CLDR release to the next, so a
string that matched before a platform upgrade may not match after it. Build the
expected text with the same formatter and locale the code uses, or assert on the
stored value and the locale passed in rather than on the rendered string.

**The layout is sized for English.** A button that fits Save has to fit
Зберегти, twice as many letters, and a menu that fits Settings has to fit the
Danish Indstillinger. Fixed widths cut the longer word off or push it onto a
second line, and often the first to see it is a reader in that market. Let
containers grow with their text, avoid fixed widths around translated words, and
review every locale at the narrowest screen the product supports.

## Further reading

- [Localization vs. Internationalization](https://www.w3.org/International/questions/qa-i18n#l10n)
  — the W3C's working definition of localization, its list of what a locale may
  need adapted, and how localization relates to internationalization.
- [Hugo: Multilingual mode](https://gohugo.io/content-management/multilingual/)
  — localized dates, currencies, numbers and percentages in Hugo templates,
  alongside translation tables and per-language URLs.
- [UTS #35, Part 3: Numbers](https://www.unicode.org/reports/tr35/tr35-numbers.html)
  — CLDR's number and currency formats, including the rule that an amount
  always travels with its currency code.
- [Unicode CLDR Project](https://cldr.unicode.org/) — the project that collects
  locale data for over 100 languages and supplies it to operating systems,
  browsers and programming languages.

<https://engineer.company/glossary/localization/>
