File formats & standardsこの記事を日本語で読む

Unicode CLDR: the locale data your formatting code should be reading, not guessing

Ask a formatting function to render a date, and it has to answer several questions that have nothing to do with translation: what order do day, month, and year go in, what separates them, is the year even the primary unit shown. These are conventions, not facts, and they differ by locale independently of language. CLDR (Unicode Common Locale Data Repository) is where the answers live: a structured, versioned dataset of locale conventions maintained as part of the Unicode project.

This article covers what CLDR actually contains, why formatting APIs are built on top of it rather than reimplementing it, and why treating locale data as static is a mistake.

What CLDR provides

CLDR is not one thing — it is a collection of related datasets, each covering a different axis of locale-specific convention:

  • Plural rules — which CLDR plural category (one, few, many, other, and so on) a given number maps to, per language; the data behind ICU MessageFormat's plural argument
  • Date and time patterns — field order, separators, which calendar system is in use, how a given locale abbreviates or spells out month and weekday names
  • Number patterns — decimal and grouping separators, currency symbol placement, percent formatting, digit shapes
  • Collation — locale-specific sort order, since alphabetical order is not universal even among languages sharing a script
  • Display names — the name of a language, script, region, or currency as it should appear when localized itself (e.g. what "French" is called in Japanese)

An example: the same date, formatted from CLDR data

None of the logic below is hardcoded per locale — the pattern, separators, and month name all come from data keyed by locale, and the same function call produces correct output for a locale it has never specifically been coded for.

const d = new Date("2026-08-15");

new Intl.DateTimeFormat("en-US").format(d); // 8/15/2026
new Intl.DateTimeFormat("ja-JP").format(d); // 2026/8/15
new Intl.DateTimeFormat("de-DE").format(d); // 15.8.2026

Why this underpins formatting APIs rather than being reimplemented per app

Standard library and platform formatting APIs — the kind exposed as Intl in JavaScript, or as equivalent locale APIs in other runtimes — are, in practice, front ends over CLDR data. The API surface is a function call; the actual knowledge of "how does German format a date" lives in CLDR, not in the API implementation itself.

This division of labor is why an application should never hardcode a formatting convention it observed for one locale and assume it generalizes. "Put the currency symbol before the number" is true for many locales and false for others; the only way to be correct across all of them is to ask the locale data rather than encode an assumption.

Why an app should read locale data instead of hardcoding conventions

The practical failure mode this prevents is subtle: a developer tests formatting in their own locale, it looks right, and the convention observed there gets hardcoded — a fixed date separator, an assumed decimal comma or period, a currency symbol always placed on the left. None of these are universal, and each one silently breaks the moment the application actually reaches a locale the developer did not test against.

Reading from CLDR-backed APIs instead of hardcoding means the correctness of your formatting scales with the breadth of CLDR's coverage rather than with how many locales a developer personally thought to test.

Locale data changes, and that is expected

CLDR is versioned and updated on a regular cycle — plural rules get refined as a language's usage is better documented, currency symbols change when currencies do, new region or script combinations are added. This is not instability in the sense of a bug; it is the dataset staying accurate as the world it describes changes.

The practical implication is that formatting output is not guaranteed to be byte-for-byte stable across CLDR versions, and code that depends on an exact formatted string (rather than on the correctness of formatting) should not assume permanence. Locale-aware formatting optimizes for being correct now, not for being frozen forever.

Related articles