Locale YAML reference
Orientation
This is the index for the canonical main/preview authoring contract under
src/Humanizer/Locales/*.yml. Use the smallest reference that answers the
question:
- File schema, inheritance, and lexical tables
- Surface fields and shared strategy values
- Words-to-number engines
- Number-to-words engines
Together with Locale YAML how-to and Add or update a locale, these references describe the checked-in locale contract without requiring contributors to reverse-engineer generator source.
Example
A regional variant declares its parent and overrides only real differences:
locale: 'en-US'
variantOf: 'en'
surfaces:
list:
engine: 'oxford'
Omitted surfaces inherit from en. A parity claim still needs a recorded
ownership chain and proving assertions for inherited surfaces.
Grammatical-case duration surface
Every terminal locale root must classify surfaces.durationCases; a regional
variant may inherit that classification only through its declared
same-language, same-script variantOf chain. An authored child block replaces
the parent's entire duration-case surface; omit the block to inherit it.
The release classifications are:
distinct: the locale has an evidenced case inventory and explicitly realizes every inventory case.invariant: the locale has an evidenced inventory whose applicable cases have the same written duration rendering.not-applicable: grammatical case is not applicable to this locale's narrowed written duration surface, with an authoritative reason.
unsupported is available only while developing locale data. Release
validation rejects every applicable unsupported root, case, and unit.
Each non-not-applicable root declares:
inventory: every case in the language's written bare-duration scope.citationCase: the inventory case whose authored base-duration phrases provide the locale's citation rendering. It is commonlynominative, but locales such as Basque useabsolutive; the generator rejects a citation case that is absent from the inventory.sources: immutable CLDR, grammar, corpus, native-authored, or native-review evidence with URL, revision, locator, and credit.cases: exactly one realization for every inventory case.
Checked-in locale YAML appends a same-line DS173237 waiver to immutable
40-hex revision values because DevSkim otherwise mistakes Git commit hashes
for secrets. The waiver applies only to that revision line and rule.
A case realization is exactly one of:
authored, with all eight units;sameRenderedAs: <case>, for an evidenced same-written-output relationship;notApplicable: <reason>, with provenance; or- development-only
unsupported: <reason>.
An authored unit is exactly one of phrase, sameRenderedAs,
notApplicable, or development-only unsupported. Case and unit aliases are
flattened during generation. Missing targets, self references, cycles, and
aliases to unavailable data fail generation.
surfaces:
durationCases:
classification: 'distinct'
inventory:
- 'nominative'
- 'dative'
citationCase: 'nominative'
sources:
cldr48:
kind: 'cldr'
url: 'https://github.com/unicode-org/cldr/blob/acd6d88ae493633240e19a87a721076a8a75c310/common/main/de.xml'
revision: 'acd6d88ae493633240e19a87a721076a8a75c310'
locator: 'long duration-unit patterns'
credit: 'Unicode CLDR contributors'
cases:
nominative:
authored: 'base-duration'
provenance: [cldr48]
dative:
authored:
units:
millisecond:
sameRenderedAs: nominative
provenance: [cldr48]
second:
phrase:
single:
numeric: '1 Sekunde'
words: 'einer Sekunde'
multiple:
forms: 'Sekunden'
provenance: [cldr48]
# minute through year are also required
provenance: [cldr48]
Case-aware calls validate the requested case against inventory before
looking up a phrase and fail clearly when it is unavailable. Existing
case-free Humanize calls continue to use the legacy duration path unchanged.
Phrase leaves use the same singular and counted-phrase grammar as
surfaces.phrases.duration. Every authored leaf supplies a singular phrase and
a numeric multiple phrase. When
surfaces.formatter.casePluralRule is explicitly declared, every reachable
named multiple form is also required for authored overlays and citation/base
duration phrases. For example, casePluralRule: arabic-cardinal requires
default, zero, dual, plural, and many; count 1 remains the separate
singular branch. Without an explicit case plural rule, normal multiple-form
fallback remains valid. The singular words variant can contain a
locale-authored one-word or article phrase; counted forms may write the count
or encode it in the unit morphology. Do not synthesize prepositions or
arbitrary number-word declension.
At runtime, HumanizeWithCase returns a bare duration phrase. Nominative
also uses the case-aware singular selection. sameRenderedAs selects the
evidenced same-language rendering; notApplicable, unsupported, an absent
requested case, or an unsupported unit throws
NotSupportedException. Missing classifications or incomplete overlays fail
generation, and no path falls back to English or invents morphology.
Case-aware duration formatting is independent of relative-date and
date-ordinal grammar.
Generator Pipeline Touchpoints
These are the main files that turn locale YAML into runtime behavior:
src/Humanizer.SourceGenerators/Common/LocaleYamlCatalog.cs- reads YAML
- resolves locale inheritance
- merges child mappings with parent mappings
- normalizes author-facing references such as
self
src/Humanizer.SourceGenerators/Common/EngineContractCatalog.cs- defines the typed generator-side structural contracts for shared engines
src/Humanizer.SourceGenerators/Generators/ProfileCatalogs/*.cs- emit typed profile catalogs for the supported surfaces
src/Humanizer.SourceGenerators/Generators/LocaleRegistryInput.cs- emits the locale-to-implementation registry wiring
src/Humanizer/Localisation/*- shared runtime kernels
Regional Variant Checklist
Before creating a new regional variant file:
- Do not use plain culture fallback as parity proof for a shipped locale.
- If the variant has no locale-owned generated overrides, do not create a YAML file.
- If the variant differs only in a few fields inside a feature block, declare
variantOfand override only those fields. - If the variant needs a different
engine, replace the whole mapping intentionally. - For parity work, inherited surfaces still need locale-specific proving assertions and a recorded inheritance chain to the terminal owner.
A locale parity claim is invalid unless every canonical surface is explicitly accounted for as locale-owned or same-language inherited with proof. There is no shipped-locale exemption list in this repo.
Exception for no-delta regional variants: A parity epic may ship minimum-valid no-delta variant files (locale: + variantOf:) when first-class matrix/sweep coverage is required. This makes the regional culture explicit in LocaleRegistrySweepTests and LocaleTheoryMatrixCompletenessTests rather than relying on implicit culture fallback. The no-delta file inherits all surfaces from the parent and produces identical output; its purpose is registry presence and test matrix coverage, not linguistic differentiation. The surfaces key may be omitted entirely for these files. Examples: ur-PK.yml and ur-IN.yml ship as no-delta variants of ur.yml.
Examples in the current repo:
en-US.ymlexists because U.S. ordinal date formatting differs fromen.ru-RU.ymldoes not need to exist whenrualready covers the generated behavior.en-IN.ymldeclaresvariantOf: 'en'and overrides only thenumber.wordsfields that genuinely differ.
Pitfall
Do not invent fields, use empty mappings as inheritance sentinels, copy a
parent file into every variant, or reference generator-internal profile keys.
Changing engine replaces that mapped block instead of merging it.
The reference describes accepted structure; it does not prove that authored words or inherited behavior are natural for a culture.
Version notes
This schema documents the canonical main/preview locale YAML compiler.
Published releases through 3.0.10 use their own checked-in localization and
package shapes. Do not backport a field merely because it appears here.