# `Localize.Locale`
[🔗](https://github.com/elixir-localize/localize/blob/v0.39.0/lib/localize/locale.ex#L1)

Locale utility functions for resolution, validation, and
per-locale data access.

## Locale data

Per-locale CLDR data (number formats, calendar patterns,
territory names, etc.) is loaded on demand via a configurable
data provider and cached in `:persistent_term`. The default
provider is `Localize.Locale.Provider.PersistentTerm`.

* `load/2` — loads raw locale data from the provider.

* `get/2` — retrieves a specific data key for a locale.

## Locale resolution

* `parent/1` — returns the CLDR parent locale (e.g.,
  `:"en-AU"` → `:en`, `:en` → `:und`).

* `cldr_locale_id_from/1` — resolves a language tag, atom, or string
  to its canonical CLDR locale identifier atom, returning an error
  for inputs that are not valid CLDR locales.

* `gettext_locale_id/2` — finds the best matching locale among
  a Gettext backend's known locales.

# `language`

```elixir
@type language() :: atom() | nil
```

A BCP 47 language subtag as an atom.

# `locale_id`

```elixir
@type locale_id() :: atom()
```

A locale identifier as an atom.

# `script`

```elixir
@type script() :: atom() | nil
```

A BCP 47 script subtag as an atom.

# `territory`

```elixir
@type territory() :: atom() | nil
```

A BCP 47 region/territory subtag as an atom.

# `cldr_locale_id_from`

```elixir
@spec cldr_locale_id_from(Localize.LanguageTag.t() | atom() | String.t() | term()) ::
  {:ok, atom()} | {:error, Exception.t()}
```

Resolves any locale input to its canonical CLDR locale id atom.

A `locale_id` is by definition a valid CLDR locale, so any input
that cannot be resolved via `Localize.validate_locale/1` is
returned as an error rather than coerced to a fresh atom. This
is the boundary that bounds atom-table growth from untrusted
locale inputs.

Pre-validated `t:Localize.LanguageTag.t/0` structs — those whose
`:cldr_locale_id` is already populated — take a fast path that
skips re-validation. Everything else routes through
`Localize.validate_locale/1`.

### Arguments

* `locale` is a locale identifier atom, string, or
  `t:Localize.LanguageTag.t/0`.

### Returns

* `{:ok, locale_id}` where `locale_id` is the canonical CLDR
  locale id atom.

* `{:error, exception}` if the locale cannot be validated.

### Examples

    iex> Localize.Locale.cldr_locale_id_from(:en)
    {:ok, :en}

    iex> Localize.Locale.cldr_locale_id_from("en-AU")
    {:ok, :"en-AU"}

    iex> {:ok, parsed} = Localize.LanguageTag.parse("fr")
    iex> Localize.Locale.cldr_locale_id_from(parsed)
    {:ok, :fr}

# `default_provider`

```elixir
@spec default_provider() :: module()
```

Returns the default locale data provider module.

### Returns

* The module implementing `Localize.Locale.Provider`.

# `display_name`

```elixir
@spec display_name(Localize.LanguageTag.t() | String.t() | atom(), Keyword.t()) ::
  {:ok, String.t()} | {:error, Exception.t()}
```

Returns the localized display name for a locale identifier.

Formats a locale identifier or language tag into a
human-readable display name using the CLDR locale display
name algorithm (e.g., `"en-AU"` → `"English (Australia)"`).

### Arguments

* `locale` is a locale identifier string, atom, or a
  `t:Localize.LanguageTag.t/0`.

* `options` is a keyword list of options.

### Options

* `:locale` is the locale to use for formatting. The default
  is `Localize.get_locale()`.

* `:prefer` is `:standard` or `:short`. The default is
  `:standard`.

### Returns

* `{:ok, name}` where `name` is the localized display name.

* `{:error, exception}` if the locale cannot be resolved.

### Examples

    iex> Localize.Locale.display_name("en-AU")
    {:ok, "English (Australia)"}

    iex> Localize.Locale.display_name("zh-Hant")
    {:ok, "Chinese (Traditional)"}

# `display_name!`

```elixir
@spec display_name!(Localize.LanguageTag.t() | String.t() | atom(), Keyword.t()) ::
  String.t()
```

Same as `display_name/2` but raises on error.

### Examples

    iex> Localize.Locale.display_name!("en-AU")
    "English (Australia)"

# `expand_locale_list`

```elixir
@spec expand_locale_list([atom() | String.t()], atom() | String.t()) :: [atom()]
```

Expands a list of locale identifiers into a list of known CLDR
locale ID atoms.

Each entry can be:

* A **CLDR locale ID atom** like `:en`, `:"fr-CA"`.

* A **coverage-level keyword** — `:basic`, `:moderate`, or
  `:modern` — that expands to all locales at or above that
  level.

* A **Gettext backend module** (a module that has called
  `use Gettext.Backend` or `use Gettext, otp_app: ...`) which
  expands to the backend's `Gettext.known_locales/1`. Each
  returned string is re-expanded as a locale ID, so POSIX-style
  names like `"pt_BR"` resolve to `:"pt-BR"`.

* A **string locale ID** like `"en"`, `"pt_BR"`, or
  `"zh_Hans"`. POSIX underscores are normalised to hyphens, then
  the string is matched against the CLDR set via likely-subtag
  resolution.

* A **wildcard string** like `"en-*"` that expands to all
  matching CLDR locales.

Invalid entries log a warning and are skipped.

### Arguments

* `entries` is a list of atoms (locale IDs, coverage keywords,
  or Gettext backend modules) and/or strings (locale IDs or
  wildcards).

* `context` is an atom or string used in warning messages to
  identify where the entry came from (e.g. `:supported_locales`).

### Returns

* A deduplicated list of locale ID atoms.

# `get`

```elixir
@spec get(Localize.Locale.Provider.locale(), list(), Keyword.t()) ::
  {:ok, term()} | {:error, term()}
```

Retrieves a value from locale data by following a list of access keys.

Delegates to the configured provider module to navigate the locale
data map.

### Arguments

* `locale` is a locale identifier atom or a `t:Localize.LanguageTag.t/0`.

* `keys` is a list of keys to traverse in the locale data map.

* `options` is a keyword list of options. The default is `[]`.

### Options

* `:provider` is the module implementing `Localize.Locale.Provider`
  to use. The default is `default_provider/0`.

* `:fallback` is a boolean. When `true`, if the requested key path
  is not found in the given locale, parent locales are searched
  according to the CLDR locale inheritance chain. The default is
  `false`.

* `:fallback_to_default` controls a final-step fallback after any
  `:fallback` parent walk. Accepts:

  * `false` (the default) — disabled.

  * `true` — use the application default locale
    (`Localize.default_locale/0`).

  * an atom, string, or `t:Localize.LanguageTag.t/0` — use that
    specific locale. The value is resolved through
    `Localize.validate_locale/1`; an invalid value returns
    `{:error, exception}` immediately.

  Composes with `:fallback` — set both to walk parents and then
  try the chosen locale.

### Returns

* `{:ok, value}` if the key path resolves to a value.

* `{:error, reason}` if the key path cannot be resolved.

# `get!`

```elixir
@spec get!(Localize.Locale.Provider.locale(), list(), Keyword.t()) ::
  term() | no_return()
```

Retrieves a value from locale data, raising on error.

Same as `get/3` but returns the value directly on success or raises
the error exception on failure.

### Arguments

* `locale` is a locale identifier atom or a `t:Localize.LanguageTag.t/0`.

* `keys` is a list of keys to traverse in the locale data map.

* `options` is a keyword list of options. See `get/3` for supported
  options.

### Returns

* The value at the requested key path.

# `gettext_locale_id`

```elixir
@spec gettext_locale_id(Localize.LanguageTag.t() | atom() | String.t(), module()) ::
  {:ok, String.t()} | {:error, Exception.t()}
```

Returns the best-matching Gettext locale for a given locale
identifier.

Compares the given locale against the locales known to a Gettext
backend using `Localize.LanguageTag.best_match/3`. This allows
a CLDR locale like `:"en-AU"` to match a Gettext locale like
`"en"` when no exact match exists.

### Arguments

* `locale` is a locale identifier atom, string, or a
  `t:Localize.LanguageTag.t/0`.

* `gettext_backend` is a module that uses `Gettext` (e.g.,
  `MyApp.Gettext`). It must respond to
  `Gettext.known_locales/1`.

### Returns

* `{:ok, gettext_locale}` where `gettext_locale` is a string
  from the Gettext backend's known locales.

* `{:error, exception}` if no match is found among the
  backend's known locales.

### Examples

    iex> Localize.Locale.gettext_locale_id(:en, Localize.Gettext)
    {:error,
     %Localize.UnknownLocaleError{
       locale_id: "en"
     }}

# `load`

```elixir
@spec load(Localize.Locale.Provider.locale(), Keyword.t()) ::
  {:ok, map()} | {:error, Exception.t()}
```

Loads locale data for the given locale.

Delegates to the configured provider module to find and retrieve
locale data.

### Arguments

* `locale` is a locale identifier atom or a `t:Localize.LanguageTag.t/0`.

* `options` is a keyword list of options. The default is `[]`.

### Options

* `:provider` is the module implementing `Localize.Locale.Provider`
  to use. The default is `default_provider/0`.

### Returns

* `{:ok, locale_data}` where `locale_data` is a map of the locale's
  CLDR data.

* `{:error, Localize.UnknownLocaleError.t()}` if the locale is not
  recognized.

# `load_and_store`

```elixir
@spec load_and_store(Localize.Locale.Provider.locale(), Keyword.t()) ::
  :ok | {:error, Exception.t()}
```

Loads and stores locale data if it has not already been loaded.

This is a convenience function that checks whether the locale
data is already available and, if not, delegates to the configured
provider to load and store it. Subsequent calls for the same
locale are no-ops.

### Arguments

* `locale` is a locale identifier atom or a `t:Localize.LanguageTag.t/0`.

* `options` is a keyword list of options. The default is `[]`.

### Options

* `:provider` is the module implementing `Localize.Locale.Provider`
  to use. The default is `default_provider/0`.

### Returns

* `:ok` if the locale data is already loaded or was successfully
  loaded and stored.

* `{:error, Localize.UnknownLocaleError.t()}` if the locale data
  could not be loaded.

# `loaded?`

```elixir
@spec loaded?(Localize.Locale.Provider.locale(), Keyword.t()) :: boolean()
```

Returns whether locale data has been loaded and is available.

Delegates to the configured provider module to check availability.

### Arguments

* `locale` is a locale identifier atom or a `t:Localize.LanguageTag.t/0`.

* `options` is a keyword list of options. The default is `[]`.

### Options

* `:provider` is the module implementing `Localize.Locale.Provider`
  to use. The default is `default_provider/0`.

### Returns

* `true` if the locale data has been loaded and stored.

* `false` otherwise.

# `locale_id_from`

```elixir
@spec locale_id_from(language(), script(), territory(), [String.t()]) :: String.t()
```

Build a locale identifier string from its component parts.

Assembles a BCP 47 locale identifier from language, script,
territory, and variant subtags, omitting nil components.

### Arguments

* `language` is a language subtag (string or atom).

* `script` is an optional script subtag (string, atom, or nil).

* `territory` is an optional territory subtag (string, atom, or nil).

* `variants` is a list of variant subtag strings.

### Returns

* A BCP 47 locale identifier string.

### Examples

    iex> Localize.Locale.locale_id_from(:en, nil, :US, [])
    "en-US"

    iex> Localize.Locale.locale_id_from(:zh, :Hant, :TW, [])
    "zh-Hant-TW"

    iex> Localize.Locale.locale_id_from(:en, nil, nil, [])
    "en"

# `locale_id_from_posix`

```elixir
@spec locale_id_from_posix(String.t()) :: String.t()
```

Convert a POSIX locale identifier to a BCP 47 locale identifier
by replacing underscores with hyphens.

### Arguments

* `locale_id` is a string locale identifier, potentially
  in POSIX format using underscores.

### Returns

* A string with underscores replaced by hyphens.

### Examples

    iex> Localize.Locale.locale_id_from_posix("en_US")
    "en-US"

    iex> Localize.Locale.locale_id_from_posix("zh_Hant_TW")
    "zh-Hant-TW"

    iex> Localize.Locale.locale_id_from_posix("en")
    "en"

# `parent`

```elixir
@spec parent(Localize.LanguageTag.t() | String.t()) ::
  {:ok, Localize.LanguageTag.t()} | {:error, Exception.t()}
```

Return the parent locale of the given language tag.

Implements the CLDR locale ID inheritance algorithm (bundle
inheritance) from
[Unicode TR35](https://www.unicode.org/reports/tr35/tr35.html#Locale_Inheritance).
The parent locale is determined by first checking the CLDR
`parentLocales` supplemental data for an explicit override,
then falling back to progressive subtag removal.

Extensions (`-u-` and `-t-`) are transferred from the child
to the parent so that calendar, numbering system, and other
preferences are preserved across the inheritance chain.

### Arguments

* `locale` is a `%Localize.LanguageTag{}` struct or a BCP 47
  locale identifier string.

### Returns

* `{:ok, parent_tag}` where `parent_tag` is a
  `%Localize.LanguageTag{}` struct representing the parent locale.

* `{:error, Localize.NoParentError.exception(locale: locale)}` if the locale
  is the root locale (`und`) which has no parent.

### Examples

    iex> {:ok, parent} = Localize.Locale.parent("en-AU")
    iex> parent.language
    :en
    iex> parent.territory
    :"001"

    iex> {:ok, parent} = Localize.Locale.parent("en")
    iex> parent.language
    :und

# `parent!`

```elixir
@spec parent!(Localize.LanguageTag.t() | String.t()) ::
  Localize.LanguageTag.t() | no_return()
```

Return the parent locale of the given language tag, or raise.

Same as `parent/1` but returns the parent `t:Localize.LanguageTag.t/0`
directly on success or raises the error exception on failure.

### Arguments

* `locale` is a `%Localize.LanguageTag{}` struct or a BCP 47
  locale identifier string.

### Returns

* A `%Localize.LanguageTag{}` representing the parent locale.

### Examples

    iex> parent = Localize.Locale.parent!("en-AU")
    iex> parent.language
    :en

# `store`

```elixir
@spec store(locale_id(), map(), Keyword.t()) :: :ok | {:error, term()}
```

Stores locale data in the provider's backing store.

Delegates to the configured provider module to persist locale data.

### Arguments

* `locale_id` is a locale identifier atom.

* `locale_data` is a map of locale data to store.

* `options` is a keyword list of options. The default is `[]`.

### Options

* `:provider` is the module implementing `Localize.Locale.Provider`
  to use. The default is `default_provider/0`.

### Returns

* `:ok` on success.

* `{:error, reason}` on failure.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
