# `Localize.Unit`
[🔗](https://github.com/elixir-localize/localize/blob/v1.2.0/lib/localize/unit.ex#L1)

Represents and formats CLDR units of measure.

A `Localize.Unit` struct holds the original unit name string,
its parsed AST representation, and an optional numeric value.
Units can be created with `new/3` and formatted with
`to_string/2`.

## Unit names

Unit names follow the CLDR identifier syntax defined in
[TR35](https://www.unicode.org/reports/tr35/tr35-general.html#unit-syntax).
Examples: `"meter"`, `"kilogram"`, `"meter-per-second"`,
`"square-kilometer"`, `"liter-per-100-kilometer"`.

## Formatting

`to_string/2` produces locale-aware output with plural-sensitive
patterns (e.g., `"1 kilometer"` vs `"3 kilometers"`) and supports
`:long`, `:short`, and `:narrow` format styles.

## Usage preferences

CLDR defines measurement usage preferences by territory and
category (e.g., road distances in the US use miles). The
`:usage` field on the struct and the `:usage` option on
`to_string/2` support automatic unit selection based on locale.

# `t`

```elixir
@type t() :: %Localize.Unit{
  format_options: Keyword.t(),
  name: String.t(),
  parsed: tuple(),
  usage: String.t() | nil,
  value: value()
}
```

# `value`

```elixir
@type value() :: number() | Decimal.t() | [number()] | nil
```

# `add`

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

Adds two convertible units.

The value of `unit_2` is converted to the unit type of `unit_1`
before addition. The result retains the unit type of `unit_1`.

### Arguments

* `unit_1` is a `%Localize.Unit{}` struct with a value.

* `unit_2` is a `%Localize.Unit{}` struct with a value.

### Returns

* `{:ok, unit}` or `{:error, exception}`.

### Examples

    iex> {:ok, a} = Localize.Unit.new(1, "kilometer")
    iex> {:ok, b} = Localize.Unit.new(500, "meter")
    iex> {:ok, result} = Localize.Unit.add(a, b)
    iex> result.value
    1.5

# `compare`

```elixir
@spec compare(t(), t()) :: :lt | :eq | :gt | {:error, Exception.t()}
```

Compares two convertible units.

The value of `unit_2` is converted to the unit type of `unit_1`
before comparison.

### Arguments

* `unit_1` is a `%Localize.Unit{}` struct with a value.

* `unit_2` is a `%Localize.Unit{}` struct with a value.

### Returns

* `:lt`, `:eq`, or `:gt`.

* `{:error, exception}` if the units are not convertible.

### Examples

    iex> {:ok, a} = Localize.Unit.new(1, "kilometer")
    iex> {:ok, b} = Localize.Unit.new(500, "meter")
    iex> Localize.Unit.compare(a, b)
    :gt

# `compatible?`

```elixir
@spec compatible?(t() | String.t(), t() | String.t()) :: boolean()
```

Returns whether two units are convertible (same category).

### Arguments

* `unit_1` is a `%Localize.Unit{}` struct or a unit name string.

* `unit_2` is a `%Localize.Unit{}` struct or a unit name string.

### Returns

* `true` or `false`.

### Examples

    iex> Localize.Unit.compatible?("meter", "foot")
    true

    iex> Localize.Unit.compatible?("meter", "kilogram")
    false

# `convert`

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

Converts a unit to a different target unit.

The source and target units must be convertible (same dimensional
base unit). Returns a new unit struct with the converted value and
the target unit type.

### Arguments

* `unit` is a `%Localize.Unit{}` struct with a value.

* `target` is the target unit identifier string (e.g., `"foot"`).

### Returns

* `{:ok, unit}` where `unit` is a new `%Localize.Unit{}` with
  the converted value and target unit type, or

* `{:error, reason}` if the unit has no value, the target cannot
  be parsed, or the units are not convertible.

### Value type

The converted value is a `Decimal` when the source value is a `Decimal`
(results stay exact) and a float otherwise. Integer and float values
both yield a float, because unit conversion is real-valued — an integer
input cannot represent a converted quantity such as `1` mile in
kilometers, so promoting to float is preferred over integer arithmetic,
which would silently lose precision. Pass a `Decimal` value when you
need an exact result.

### Examples

    iex> {:ok, meters} = Localize.Unit.new(1, "kilometer")
    iex> {:ok, result} = Localize.Unit.convert(meters, "meter")
    iex> result.value
    1000.0
    iex> result.name
    "meter"

    iex> {:ok, tonnes} = Localize.Unit.new(Decimal.new(1), "tonne")
    iex> {:ok, result} = Localize.Unit.convert(tonnes, "kilogram")
    iex> match?(%Decimal{}, result.value)
    true

# `convert!`

```elixir
@spec convert!(t(), String.t()) :: t() | no_return()
```

Converts a unit to a different target unit, raising on error.

Same as `convert/2` but returns the unit struct directly or raises
`ArgumentError`.

### Arguments

* `unit` is a `%Localize.Unit{}` struct with a value.

* `target` is the target unit identifier string.

### Returns

* A `%Localize.Unit{}` struct with the converted value.

### Examples

    iex> unit = Localize.Unit.new!(1000, "meter")
    iex> result = Localize.Unit.convert!(unit, "kilometer")
    iex> result.value
    1.0

# `convert_measurement_system`

```elixir
@spec convert_measurement_system(t(), :metric | :us | :uk) ::
  {:ok, t()} | {:error, String.t()}
```

Converts a unit to the preferred unit for a given measurement system.

Looks up the CLDR unit preference data for the unit's quantity
category and the specified measurement system, then converts to
the first preferred unit for the "default" usage.

### Arguments

* `unit` is a `%Localize.Unit{}` struct with a value.

* `system` is the target measurement system: `:metric`, `:us`, or `:uk`.

### Returns

* `{:ok, unit}` where `unit` is a new `%Localize.Unit{}` with the
  converted value and the preferred unit for that system, or

* `{:error, reason}` if the unit has no value, the measurement
  system is invalid, or no preference is found.

### Examples

    iex> {:ok, meters} = Localize.Unit.new(100, "meter")
    iex> {:ok, result} = Localize.Unit.convert_measurement_system(meters, :us)
    iex> result.name
    "mile"

# `decompose`

```elixir
@spec decompose(t(), [String.t()], Keyword.t()) ::
  {:ok, [t()]} | {:error, Exception.t()}
```

Decomposes a unit into a list of units with the given target units.

This is used for mixed-unit formatting, for example converting
1.75 meters into `[1 meter, 75 centimeters]` or 5.25 feet into
`[5 feet, 3 inches]`.

Each target unit receives the integer part of the remaining value,
except the last unit which receives the remainder.

When the input value is a `Decimal`, the truncate/remainder math runs
in `Decimal` arithmetic (not float), so precision is preserved
through the decomposition.

Intermediate units whose integer part rounds to zero are skipped (so
decomposing 1.5 m into `["meter", "kilometer", "centimeter"]` skips
the kilometer entry). The trailing unit is also skipped if its value
is zero.

When `format_options` is given, those options are attached to the
final (trailing) unit's `:format_options` field — typically a
`[round_nearest: N]` skeleton lifted from CLDR's preference data.
`Localize.Unit.localize/2` uses this to thread CLDR's skeletons
through to `to_string/2`.

### Arguments

* `unit` is a `%Localize.Unit{}` struct with a value.

* `target_units` is a list of unit name strings, ordered from
  largest to smallest (e.g., `["foot", "inch"]`).

* `format_options` is an optional keyword list attached to the final
  unit's `:format_options`. Defaults to `[]`.

### Options

The options are not interpreted by this function; they are attached
verbatim to the trailing unit's `:format_options` field and merged
into any later `to_string/2` call for that unit. The typical content
is `[round_nearest: n]` lifted from CLDR's unit preference skeletons.
See `to_string/2` for the options that formatting supports.

### Returns

* `{:ok, units}` where `units` is a list of `%Localize.Unit{}` structs.

* `{:error, exception}` if conversion fails.

### Examples

    iex> {:ok, m} = Localize.Unit.new(1.75, "meter")
    iex> {:ok, parts} = Localize.Unit.decompose(m, ["meter", "centimeter"])
    iex> Enum.map(parts, fn u -> {u.name, u.value} end)
    [{"meter", 1}, {"centimeter", 75.0}]

    iex> {:ok, m} = Localize.Unit.new(Decimal.new("1.75"), "meter")
    iex> {:ok, parts} = Localize.Unit.decompose(m, ["meter", "centimeter"])
    iex> Enum.map(parts, fn u -> {u.name, u.value} end)
    [{"meter", 1}, {"centimeter", Decimal.new("75.0")}]

    iex> {:ok, m} = Localize.Unit.new(2.0, "meter")
    iex> {:ok, parts} = Localize.Unit.decompose(m, ["meter", "centimeter"])
    iex> Enum.map(parts, fn u -> u.name end)
    ["meter"]

    iex> {:ok, m} = Localize.Unit.new(0.5, "meter")
    iex> {:ok, parts} = Localize.Unit.decompose(m, ["meter", "centimeter"])
    iex> Enum.map(parts, fn u -> u.name end)
    ["centimeter"]

    iex> {:ok, m} = Localize.Unit.new(1.83, "meter")
    iex> {:ok, parts} = Localize.Unit.decompose(m, ["foot", "inch"], round_nearest: 1)
    iex> List.last(parts).format_options
    [round_nearest: 1]

# `define_unit`

```elixir
@spec define_unit(String.t(), map()) :: :ok | {:error, String.t()}
```

Registers a custom unit definition at runtime.

Custom units extend the built-in CLDR unit database with user-defined
units. Each custom unit must specify a base unit it converts to, a
conversion factor, and a category.

### Arguments

* `name` is a string unit identifier (e.g., `"smoot"`). Must start with
  a lowercase letter and contain only lowercase letters, digits, and hyphens.

* `definition` is a map with the following keys:

### Options

* `:base_unit` (required) — the CLDR base unit this custom unit converts
  to (e.g., `"meter"`, `"kilogram"`).

* `:factor` (required) — conversion multiplier:
  `1 custom_unit = factor * base_unit`.

* `:offset` (optional) — additive offset for temperature-like conversions.
  Defaults to `0.0`.

* `:category` (required) — the unit category (e.g., `"length"`, `"mass"`).

* `:display` (optional) — locale-specific display patterns. A nested map
  of `locale => style => plural_patterns`.

### Returns

* `:ok` on success.

* `{:error, reason}` if validation fails.

### Examples

    iex> Localize.Unit.define_unit("smoot", %{
    ...>   base_unit: "meter",
    ...>   factor: 1.7018,
    ...>   category: "length",
    ...>   display: %{en: %{long: %{one: "{0} smoot", other: "{0} smoots"}}}
    ...> })
    :ok

# `display_name`

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

Returns the localized stand-alone display name of a unit.

This is the CLDR `<displayName>` form — a bare label like
"meters" or "Meter", suitable for headings, form-field labels,
or column titles. It is distinct from the value-bearing inline
forms returned by `to_string/2`, which are pluralised and
grammatical-case aware (e.g., "5 meters"). If a
`t:Localize.Unit.t/0` struct carrying a value is passed, the
value is ignored.

### Arguments

* `unit` is a `%Localize.Unit{}` struct or a unit name string.

* `options` is a keyword list of options.

### Options

* `:locale` is a locale identifier. The default is `Localize.get_locale()`.

* `:style` is `:long`, `:short`, or `:narrow`. The default is `:long`.

### Returns

* `{:ok, name}` or `{:error, exception}`.

### Examples

    iex> Localize.Unit.display_name("meter", locale: :en)
    {:ok, "meters"}

    iex> Localize.Unit.display_name("meter", locale: :en, style: :short)
    {:ok, "m"}

# `display_name!`

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

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

### Arguments

* `unit_or_name` is a `t:Localize.Unit.t/0` or a unit name string.

* `options` is a keyword list of options.

### Options

See `display_name/2` for the supported options.

### Returns

* The localized display name string.

* Raises an exception if the display name cannot be produced.

### Examples

    iex> Localize.Unit.display_name!("meter", locale: :en)
    "meters"

    iex> Localize.Unit.display_name!("meter", locale: :en, style: :short)
    "m"

# `div`

```elixir
@spec div(t(), number() | t()) :: {:ok, t()} | {:error, Exception.t()}
```

Divides a unit by a scalar or another unit.

When divided by a number, the value is scaled. When divided by
another unit, a per-unit compound is produced (e.g., meter ÷ second).

### Arguments

* `unit` is a `%Localize.Unit{}` struct.

* `divisor` is a number or a `%Localize.Unit{}` struct.

### Returns

* `{:ok, unit}` or `{:error, exception}`.

### Examples

    iex> {:ok, m} = Localize.Unit.new(10, "meter")
    iex> {:ok, result} = Localize.Unit.div(m, 2)
    iex> result.value
    5.0

# `humanize`

```elixir
@spec humanize(t(), Keyword.t()) :: {:ok, t()} | {:error, Exception.t()}
```

Converts a bit-, byte-, hertz- or watt-based unit to the prefixed
unit that best fits its magnitude — human-readable file sizes,
frequencies and power figures.

Selects the largest SI prefix (kilobyte, megahertz, gigawatt, ...)
or, for bits and bytes, IEC binary prefix (kibibyte, mebibyte, ...)
such that the converted value is at least `1`. Values smaller than
one kilo-unit (or kibi-unit) are returned unchanged.

These bases are the locale-invariant SI-prefixed quantities that
CLDR's unit preference data does not cover. For physical
quantities such as length or mass, use the `:usage` option on
`to_string/2` instead — CLDR preferences pick the display unit by
territory and magnitude.

### Arguments

* `unit` is a `%Localize.Unit{}` struct with a value, based on
  `"bit"`, `"byte"`, `"hertz"` or `"watt"` (a bare or
  already-prefixed unit such as `"byte"`, `"kilobyte"` or
  `"megahertz"`).

* `options` is a keyword list of options.

### Options

* `:system` is the prefix system to scale with: `:si` (powers of
  1000, the default) or `:iec` (powers of 1024, bit- and
  byte-based units only). Note that CLDR provides compact display
  patterns (like `"MB"`) only for SI-prefixed units; IEC units
  format with their full names in all format widths.

### Returns

* `{:ok, unit}` where `unit` is a new `%Localize.Unit{}` with the
  scaled value and prefixed unit name, or

* `{:error, reason}` if the unit has no value, has an unsupported
  base unit, or the prefix system is invalid or does not apply to
  the base unit.

### Examples

    iex> {:ok, unit} = Localize.Unit.new(1_500_000, "byte")
    iex> {:ok, humanized} = Localize.Unit.humanize(unit)
    iex> {humanized.name, humanized.value}
    {"megabyte", 1.5}
    iex> Localize.Unit.to_string(humanized, format: :narrow, locale: :en)
    {:ok, "1.5MB"}

    iex> {:ok, unit} = Localize.Unit.new(2_500_000_000, "hertz")
    iex> {:ok, humanized} = Localize.Unit.humanize(unit)
    iex> {humanized.name, humanized.value}
    {"gigahertz", 2.5}

    iex> {:ok, unit} = Localize.Unit.new(1_048_576, "byte")
    iex> {:ok, humanized} = Localize.Unit.humanize(unit, system: :iec)
    iex> {humanized.name, humanized.value}
    {"mebibyte", 1.0}

# `humanize!`

```elixir
@spec humanize!(t(), Keyword.t()) :: t() | no_return()
```

Converts a bit-, byte-, hertz- or watt-based unit to the prefixed
unit that best fits its magnitude, raising on error.

See `humanize/2` for details.

### Arguments

* `unit` is a `%Localize.Unit{}` struct with a value, based on
  `"bit"`, `"byte"`, `"hertz"` or `"watt"`.

* `options` is a keyword list of options. See `humanize/2`.

### Returns

* A new `%Localize.Unit{}` with the scaled value and prefixed
  unit name, or

* raises an exception if the unit has no value, has an unsupported
  base unit, or the prefix system is invalid.

### Examples

    iex> Localize.Unit.new!(2_750_000_000, "byte")
    ...> |> Localize.Unit.humanize!()
    ...> |> Localize.Unit.to_string!(format: :narrow, fractional_digits: 1, locale: :en)
    "2.8GB"

# `known_categories`

```elixir
@spec known_categories() :: [String.t()]
```

Returns a list of all known unit categories.

### Returns

* A list of category name strings.

### Examples

    iex> cats = Localize.Unit.known_categories()
    iex> "length" in cats
    true

# `known_units_by_category`

```elixir
@spec known_units_by_category() :: %{required(String.t()) =&gt; [String.t()]}
```

Returns a map of unit categories to their member units.

### Returns

* A map of `%{category_string => [unit_name_string]}`.

### Examples

    iex> by_cat = Localize.Unit.known_units_by_category()
    iex> is_list(by_cat["length"])
    true

# `known_usages`

```elixir
@spec known_usages() :: [String.t()]
```

Returns a list of valid usage types for unit preferences.

### Returns

* A sorted list of usage atoms.

### Examples

    iex> usages = Localize.Unit.known_usages()
    iex> "default" in usages
    true

# `load_custom_units`

```elixir
@spec load_custom_units(String.t()) :: {:ok, non_neg_integer()} | {:error, String.t()}
```

Loads custom unit definitions from an Elixir term file (`.exs`).

The file must evaluate to a list of maps, each with a `:unit` key
and the standard definition fields (`:base_unit`, `:factor`,
`:category`, and optionally `:display`).

> #### Security {: .warning}
>
> This function uses `Code.eval_file/1` to evaluate the given
> file, which executes arbitrary Elixir code. Only load files
> from trusted sources. Never call this function with unsanitised
> user input or paths derived from external data.

### Arguments

* `path` is the path to the `.exs` file.

### Returns

* `{:ok, count}` with the number of units loaded.

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

### Examples

    iex> {:error, message} = Localize.Unit.load_custom_units("no/such/file.exs")
    iex> message =~ "file not found"
    true

# `localize`

```elixir
@spec localize(t(), Keyword.t()) :: {:ok, [t()]} | {:error, Exception.t()}
```

Converts a unit into the locale-preferred unit set for a given usage.

Resolves the preferred unit list with `Localize.Unit.Preference.preferred_units/2`
(driven by `:usage` and `:territory`), then decomposes the value across
that list. The result is a list of `t:Localize.Unit.t/0` structs ready
to be rendered with `to_string/2`.

This is the convenience wrapper for the common
preference-resolution → conversion → decomposition pipeline. Pass the
returned list straight to `Localize.Unit.to_string/2` to render it as
a localized unit list (e.g. `"6 feet, 0.047 inches"`).

### Arguments

* `unit` is a `t:Localize.Unit.t/0` struct with a value.

* `options` is a keyword list of options.

### Options

* `:usage` is the unit's intended usage. Accepts an atom
  (`:person_height`) or a CLDR-style string (`"person-height"`).
  If omitted, falls back to the struct's `:usage` field, then to
  `:default`.

* `:territory` is a territory code atom (e.g. `:US`, `:DE`). If
  omitted, derived from the current locale.

* `:locale` is a locale identifier used to derive `:territory` when
  `:territory` is not given.

### Returns

* `{:ok, units}` where `units` is a list of `t:Localize.Unit.t/0`
  structs, in preference order (largest unit first).

* `{:error, exception}` if preferences cannot be resolved or the
  decomposition fails.

### Examples

    iex> unit = Localize.Unit.new!(1.83, "meter")
    iex> {:ok, parts} = Localize.Unit.localize(unit, usage: :person_height, territory: :US)
    iex> Enum.map(parts, fn u -> u.name end)
    ["foot", "inch"]

    iex> unit = Localize.Unit.new!(2_000, "meter")
    iex> {:ok, [km]} = Localize.Unit.localize(unit, usage: :road, territory: :DE)
    iex> {km.name, km.value}
    {"kilometer", 2.0}

# `measurement_system_for_territory`

```elixir
@spec measurement_system_for_territory(atom(), :default | :temperature | :paper_size) ::
  atom()
```

Returns the preferred measurement system for a territory.

### Arguments

* `territory` is a territory code atom (e.g., `:US`, `:GB`).

### Returns

* `:metric`, `:us`, or `:uk`.

### Examples

    iex> Localize.Unit.measurement_system_for_territory(:US)
    :us

    iex> Localize.Unit.measurement_system_for_territory(:FR)
    :metric

    iex> Localize.Unit.measurement_system_for_territory(:US, :temperature)
    :us

    iex> Localize.Unit.measurement_system_for_territory(:LR, :temperature)
    :metric

    iex> Localize.Unit.measurement_system_for_territory(:US, :paper_size)
    :us_letter

    iex> Localize.Unit.measurement_system_for_territory(:FR, :paper_size)
    :a4

# `mult`

```elixir
@spec mult(t(), number() | t()) :: {:ok, t()} | {:error, Exception.t()}
```

Multiplies a unit by a scalar or another unit.

When multiplied by a number, the value is scaled. When multiplied
by another unit, a compound unit is produced (e.g., meter × second).

### Arguments

* `unit` is a `%Localize.Unit{}` struct.

* `multiplier` is a number or a `%Localize.Unit{}` struct.

### Returns

* `{:ok, unit}` or `{:error, exception}`.

### Examples

    iex> {:ok, m} = Localize.Unit.new(5, "meter")
    iex> {:ok, result} = Localize.Unit.mult(m, 3)
    iex> result.value
    15

# `negate`

```elixir
@spec negate(t()) :: {:ok, t()} | {:error, Exception.t()}
```

Negates a unit's value.

### Arguments

* `unit` is a `%Localize.Unit{}` struct with a value.

### Returns

* `{:ok, unit}` or `{:error, exception}`.

### Examples

    iex> {:ok, m} = Localize.Unit.new(10, "meter")
    iex> {:ok, negated} = Localize.Unit.negate(m)
    iex> negated.value
    -10

# `new`

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

Creates a new unit from a CLDR unit identifier string without a value.

### Arguments

* `name` is a unit identifier string such as `"meter-per-second"`.

### Returns

* `{:ok, unit}` where `unit` is a `%Localize.Unit{}` struct, or

* `{:error, reason}` if the identifier cannot be parsed.

### Examples

    iex> {:ok, unit} = Localize.Unit.new("meter")
    iex> unit.name
    "meter"

# `new`

```elixir
@spec new(number() | Decimal.t(), String.t(), keyword()) ::
  {:ok, t()} | {:error, Exception.t()}
```

Creates a new unit with a value and a CLDR unit identifier string.

### Arguments

* `amount` is the numeric value (integer, float, or Decimal).

* `unit` is a unit identifier string such as `"meter-per-second"`.

* `options` is an optional keyword list.

### Options

* `:usage` is a string specifying the intended usage context for
  the unit. Valid values include `"default"`, `"person"`,
  `"person-height"`, `"road"`, `"food"`, `"vehicle-fuel"`,
  and others defined in the CLDR unit preference data. The usage
  affects which target unit is selected when calling
  `convert_measurement_system/2`.

### Returns

* `{:ok, unit}` where `unit` is a `%Localize.Unit{}` struct, or

* `{:error, reason}` if the value is not a valid number, the
  identifier cannot be parsed, or the usage is invalid.

### Examples

    iex> {:ok, unit} = Localize.Unit.new(100, "meter")
    iex> unit.value
    100
    iex> unit.name
    "meter"

    iex> {:ok, unit} = Localize.Unit.new(Decimal.new("3.14"), "kilogram")
    iex> unit.value
    Decimal.new("3.14")

    iex> {:ok, unit} = Localize.Unit.new(180, "centimeter", usage: "person-height")
    iex> unit.usage
    "person-height"

# `new!`

```elixir
@spec new!(String.t()) :: t() | no_return()
```

Creates a new unit from a CLDR unit identifier string, raising on error.

Same as `new/1` but returns the struct directly or raises
`ArgumentError`.

### Arguments

* `name` is a unit identifier string.

### Returns

* A `%Localize.Unit{}` struct.

### Examples

    iex> unit = Localize.Unit.new!("meter")
    iex> unit.name
    "meter"

# `new!`

```elixir
@spec new!(number() | Decimal.t(), String.t(), keyword()) :: t() | no_return()
```

Creates a new unit with a value and a CLDR unit identifier string,
raising on error.

Same as `new/2` but returns the struct directly or raises
`ArgumentError`.

### Arguments

* `amount` is the numeric value (integer, float, or Decimal).

* `unit` is a unit identifier string.

* `options` is a keyword list of options.

### Options

See `new/3` for the supported options.

### Returns

* A `%Localize.Unit{}` struct.

### Examples

    iex> unit = Localize.Unit.new!(42, "kilogram")
    iex> unit.value
    42

# `parse`

```elixir
@spec parse(String.t(), Keyword.t()) :: {:ok, t()} | {:error, Exception.t()}
```

Parses a string containing a number and a unit name into a unit.

The number is parsed with the locale's number symbols and the unit name is matched against the locale's unit names — display names and the text of the long, short and narrow patterns — as well as the canonical CLDR identifiers and any units registered with `define_unit/2`. Matching is case-insensitive; when a name is ambiguous ("2 w" matches both watt and week via their narrow forms) the candidates are filtered by the `:only`/`:except` options and the first remaining unit in alphabetical order is chosen.

### Arguments

* `unit_string` is a string containing a number and a unit name, in either order (e.g., `"1kg"`, `"1 kilogram"`, `"2,5 kg"` in a locale with comma decimals).

* `options` is a keyword list of options.

### Options

* `:locale` is a locale identifier atom, string, or a `t:Localize.LanguageTag.t/0`. The default is `Localize.get_locale()`. Both the number symbols and the unit names follow this locale.

* `:only` is a category, a unit name, or a list of either (atoms or strings). Only matching candidates are considered: `only: :duration` resolves `"2w"` to weeks rather than watts.

* `:except` is the complement of `:only`: matching candidates are excluded.

### Returns

* `{:ok, unit}` where `unit` is a `t:t/0` with the parsed value.

* `{:error, exception}` if no number is found, the unit name is unknown, or the filters exclude every candidate.

### Examples

    iex> Localize.Unit.parse("1kg")
    Localize.Unit.new(1, "kilogram")

    iex> Localize.Unit.parse("1 kilogram")
    Localize.Unit.new(1, "kilogram")

    iex> Localize.Unit.parse("2w", only: :duration)
    Localize.Unit.new(2, "week")

    iex> Localize.Unit.parse("2 Tage", locale: :de)
    Localize.Unit.new(2, "day")

    iex> {:error, %Localize.UnknownUnitError{}} = Localize.Unit.parse("1 blorb")

# `parse!`

```elixir
@spec parse!(String.t(), Keyword.t()) :: t() | no_return()
```

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

### Arguments

* `unit_string` is a string containing a number and a unit name.

* `options` is a keyword list of options. See `parse/2`.

### Returns

* A `t:t/0` with the parsed value.

### Raises

* Raises an exception if the string cannot be parsed.

### Examples

    iex> Localize.Unit.parse!("1kg").name
    "kilogram"

# `parse_unit_name`

```elixir
@spec parse_unit_name(String.t(), Keyword.t()) ::
  {:ok, String.t()} | {:error, Exception.t()}
```

Parses a unit name string into its canonical unit identifier.

Like `parse/2` but for a bare unit name with no numeric value: `"kg"` resolves to `"kilogram"`. The same locale-aware matching and `:only`/`:except` disambiguation apply.

### Arguments

* `unit_name_string` is a unit name string (e.g., `"kg"`, `"kilograms"`, `"Tage"`).

* `options` is a keyword list of options. See `parse/2`.

### Returns

* `{:ok, unit_name}` where `unit_name` is the canonical unit identifier string.

* `{:error, exception}` if the name is unknown or the filters exclude every candidate.

### Examples

    iex> Localize.Unit.parse_unit_name("kg")
    {:ok, "kilogram"}

    iex> Localize.Unit.parse_unit_name("w", only: :duration)
    {:ok, "week"}

    iex> Localize.Unit.parse_unit_name("Tage", locale: :de)
    {:ok, "day"}

# `parse_unit_name!`

```elixir
@spec parse_unit_name!(String.t(), Keyword.t()) :: String.t() | no_return()
```

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

### Arguments

* `unit_name_string` is a unit name string.

* `options` is a keyword list of options. See `parse_unit_name/2`.

### Returns

* The canonical unit identifier string.

### Raises

* Raises an exception if the name cannot be resolved.

### Examples

    iex> Localize.Unit.parse_unit_name!("kg")
    "kilogram"

# `sub`

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

Subtracts `unit_2` from `unit_1`.

The value of `unit_2` is converted to the unit type of `unit_1`.

### Arguments

* `unit_1` is a `%Localize.Unit{}` struct with a value.

* `unit_2` is a `%Localize.Unit{}` struct with a value.

### Returns

* `{:ok, unit}` or `{:error, exception}`.

### Examples

    iex> {:ok, a} = Localize.Unit.new(5, "kilometer")
    iex> {:ok, b} = Localize.Unit.new(2000, "meter")
    iex> {:ok, result} = Localize.Unit.sub(a, b)
    iex> result.value
    3.0

# `to_iolist`

```elixir
@spec to_iolist(t(), Keyword.t()) :: {:ok, iolist()} | {:error, Exception.t()}
```

Formats a unit as an iolist.

Same as `to_string/2` but returns an iolist for efficient
IO operations without an intermediate binary allocation.

### Arguments

* `unit` is a `%Localize.Unit{}` struct.

* `options` is a keyword list of formatting options (same as `to_string/2`).

### Options

See `to_string/2` for the supported options.

### Returns

* `{:ok, iolist}` or `{:error, exception}`.

### Examples

    iex> {:ok, unit} = Localize.Unit.new(42, "meter")
    iex> Localize.Unit.to_iolist(unit)
    {:ok, ["42 meters"]}

# `to_parts`

```elixir
@spec to_parts(t(), Keyword.t()) ::
  {:ok, [%{type: atom(), value: String.t()}]} | {:error, Exception.t()}
```

Formats a unit into a list of typed parts, mirroring ECMA-402's `formatToParts` for unit style.

The parts concatenate to exactly the string `to_string/2` produces with the same options. The number's segments come from `Localize.Number.to_parts/2`; the unit pattern text is tagged `:unit` with its surrounding whitespace as `:literal` parts.

### Arguments

* `unit` is a `t:Localize.Unit.t/0` struct.

* `options` is a keyword list of options.

### Options

* `:locale` is a locale identifier. The default is `Localize.get_locale/0`.

* `:format` is `:long`, `:short`, or `:narrow`. The default is `:long`.

* The number-formatting options of `to_string/2` apply to the numeric segments.

### Returns

* `{:ok, parts}` where `parts` is a list of `%{type: atom(), value: String.t()}` maps.

* `{:error, exception}` if the unit has no direct CLDR pattern or the options are invalid.

### Examples

    iex> {:ok, unit} = Localize.Unit.new(42, "meter")
    iex> Localize.Unit.to_parts(unit, locale: :en)
    {:ok,
     [
       %{type: :integer, value: "42"},
       %{type: :literal, value: " "},
       %{type: :unit, value: "meters"}
     ]}

# `to_parts!`

```elixir
@spec to_parts!(t(), Keyword.t()) :: [%{type: atom(), value: String.t()}]
```

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

### Arguments

* `unit` is a `t:Localize.Unit.t/0` struct.

* `options` is a keyword list of options. See `to_parts/2`.

### Returns

* A list of `%{type: atom(), value: String.t()}` maps.

### Raises

* Raises an exception if the unit cannot be decomposed into parts.

### Examples

    iex> Localize.Unit.to_parts!(Localize.Unit.new!(42, "meter"), locale: :en) |> length()
    3

# `to_range_parts`

```elixir
@spec to_range_parts(t(), t(), Keyword.t()) ::
  {:ok, [%{type: atom(), value: String.t(), source: atom()}]}
  | {:error, Exception.t()}
```

Formats two units of the same kind into typed range parts, mirroring ECMA-402's `formatRangeToParts` for unit style.

The parts concatenate to exactly the string `to_range_string/3` produces with the same options. The numeric range's parts keep their `:start_range` / `:end_range` / `:shared` sources; the unit pattern's text carries source `:shared` with the unit name tagged `:unit`.

### Arguments

* `unit_start` is a `t:Localize.Unit.t/0` struct for the range start.

* `unit_end` is a `t:Localize.Unit.t/0` struct for the range end. Must have the same unit name as `unit_start`.

* `options` is a keyword list of options. See `to_range_string/3`.

### Returns

* `{:ok, parts}` where `parts` is a list of `%{type: atom(), value: String.t(), source: atom()}` maps.

* `{:error, exception}` if the units differ, the unit has no direct CLDR pattern, or the options are invalid.

### Examples

    iex> {:ok, unit_start} = Localize.Unit.new(2, "kilometer")
    iex> {:ok, unit_end} = Localize.Unit.new(5, "kilometer")
    iex> Localize.Unit.to_range_parts(unit_start, unit_end, locale: :en)
    {:ok,
     [
       %{type: :integer, value: "2", source: :start_range},
       %{type: :literal, value: "–", source: :shared},
       %{type: :integer, value: "5", source: :end_range},
       %{type: :literal, value: " ", source: :shared},
       %{type: :unit, value: "kilometers", source: :shared}
     ]}

# `to_range_parts!`

```elixir
@spec to_range_parts!(t(), t(), Keyword.t()) :: [
  %{type: atom(), value: String.t(), source: atom()}
]
```

Same as `to_range_parts/3` but raises on error.

### Arguments

* `unit_start` is a `t:Localize.Unit.t/0` struct for the range start.

* `unit_end` is a `t:Localize.Unit.t/0` struct for the range end.

* `options` is a keyword list of options. See `to_range_parts/3`.

### Returns

* A list of `%{type: atom(), value: String.t(), source: atom()}` maps.

### Raises

* Raises an exception if the range cannot be decomposed into parts.

### Examples

    iex> Localize.Unit.to_range_parts!(Localize.Unit.new!(2, "kilometer"), Localize.Unit.new!(5, "kilometer"), locale: :en) |> length()
    5

# `to_range_string`

```elixir
@spec to_range_string(t(), t(), Keyword.t()) ::
  {:ok, String.t()} | {:error, Exception.t()}
```

Formats two units of the same kind as a localized range.

The unit pattern is applied once to the formatted numeric range per TR35: the pattern's plural category comes from the TR35 plural-range rules (`Localize.Number.PluralRule.Range`), so "0–1 jour" is singular in French while "1–2 jours" is plural.

### Arguments

* `unit_start` is a `t:Localize.Unit.t/0` struct for the range start.

* `unit_end` is a `t:Localize.Unit.t/0` struct for the range end. Must have the same unit name as `unit_start`.

* `options` is a keyword list of options.

### Options

* `:locale` is a locale identifier. The default is `Localize.get_locale/0`.

* `:format` is `:long`, `:short`, or `:narrow`. The default is `:long`.

* The number-formatting options of `to_string/2` (`:fractional_digits`, `:rounding_mode`, and friends) apply to both endpoints.

### Returns

* `{:ok, formatted_string}` on success.

* `{:error, exception}` if the units differ, the unit has no direct CLDR pattern, or the options are invalid.

### Examples

    iex> {:ok, unit_start} = Localize.Unit.new(2, "kilometer")
    iex> {:ok, unit_end} = Localize.Unit.new(5, "kilometer")
    iex> Localize.Unit.to_range_string(unit_start, unit_end, locale: :en)
    {:ok, "2–5 kilometers"}

    iex> {:ok, unit_start} = Localize.Unit.new(2, "kilometer")
    iex> {:ok, unit_end} = Localize.Unit.new(5, "kilometer")
    iex> Localize.Unit.to_range_string(unit_start, unit_end, locale: :en, format: :short)
    {:ok, "2–5 km"}

# `to_range_string!`

```elixir
@spec to_range_string!(t(), t(), Keyword.t()) :: String.t()
```

Same as `to_range_string/3` but raises on error.

### Arguments

* `unit_start` is a `t:Localize.Unit.t/0` struct for the range start.

* `unit_end` is a `t:Localize.Unit.t/0` struct for the range end.

* `options` is a keyword list of options. See `to_range_string/3`.

### Returns

* A formatted string.

### Raises

* Raises an exception if the range cannot be formatted.

### Examples

    iex> Localize.Unit.to_range_string!(Localize.Unit.new!(2, "kilometer"), Localize.Unit.new!(5, "kilometer"), locale: :en)
    "2–5 kilometers"

# `to_string`

```elixir
@spec to_string(t() | [t(), ...], Keyword.t()) ::
  {:ok, String.t()} | {:error, Exception.t()}
```

Formats a unit together with its value as a localized string.

Accepts either a single `t:Localize.Unit.t/0` struct or a list of them.
A single unit is rendered inline with its value using the locale's
pluralised pattern (e.g., `"5 meters"`). A list is rendered by
formatting each element and joining via `Localize.List.to_string/2`,
which is what produces mixed-unit output like `"6 feet, 0.047 inches"`.

When `:usage` is supplied for a *single* unit — or the unit's struct
carries a non-`nil` `:usage` field set at `new/3` time — this function
first calls `localize/2` to convert the value into the locale-preferred
unit set, then formats the resulting list. This is the one-call
shortcut for the
preference-resolution → conversion → decomposition → format pipeline.

The territory used for preference resolution is derived from the
`:locale` option (via `Localize.Territory.territory_from_locale/1`).
There is no `:territory` option on `to_string/2`; if you need to
override the territory independently of the locale, call
`localize/2` directly and pipe the result back into `to_string/2`.

For the bare stand-alone unit label without a value, use `display_name/2`.

### Arguments

* `unit_or_units` is a `t:Localize.Unit.t/0` struct or a non-empty
  list of `t:Localize.Unit.t/0` structs.

* `options` is a keyword list of options.

### Options

* `:locale` is a locale identifier atom, string, or a
  `t:Localize.LanguageTag.t/0`. The default is the current process
  locale.

* `:format` is `:long`, `:short`, or `:narrow`.
  The default is `:long`.

* `:humanize` (single unit only) when `true` renders the unit in
  the unit people expect for its magnitude: bit-, byte-, hertz-
  and watt-based units are scaled through `humanize/2` (honouring
  the `:system` option for `:si` or `:iec` prefixes), and all
  other units are rendered through the usage-based preference
  pipeline using the struct's `:usage` field or `:default`. An
  explicit `:usage` option takes precedence for preference-scaled
  units. The default is `false`.

* `:usage` (single unit only) is the unit's intended usage. When
  given, triggers `localize/2` before formatting. Accepts an atom
  (`:person_height`) or a CLDR-style string (`"person-height"`). If
  the option is omitted but the struct's `:usage` field is set,
  `localize/2` is still triggered using that value.

* `:list_options` is a keyword list passed to `Localize.List.to_string/2`
  when rendering a unit list. Use it to pick a unit-specific list style
  (e.g. `list_options: [list_style: :unit_short]`).

* `:grammatical_case` selects a case-keyed pattern variant for the
  unit (e.g. `:nominative`, `:dative`, `:genitive`). Defaults to
  `:nominative`.

* `:grammatical_gender` is accepted on the public API for cldr_units
  parity. Only meaningful for compound-unit patterns
  (`compound_unit_pattern` keyed by gender); for simple units the
  gender is a fixed property of the unit in CLDR data and the option
  has no effect on output.

* `:backend` is `:nif` or `:elixir`. When `:nif` is specified and the
  NIF is available, ICU4C is used for formatting. Otherwise falls
  back to the pure-Elixir formatter. The default is `:elixir`.

### Returns

* `{:ok, formatted_string}` on success.

* `{:error, exception}` if the unit cannot be formatted.

### Examples

    iex> {:ok, unit} = Localize.Unit.new(42, "meter")
    iex> Localize.Unit.to_string(unit)
    {:ok, "42 meters"}

    iex> {:ok, unit} = Localize.Unit.new(1, "meter")
    iex> Localize.Unit.to_string(unit)
    {:ok, "1 meter"}

    iex> {:ok, unit} = Localize.Unit.new(42, "meter")
    iex> Localize.Unit.to_string(unit, format: :short)
    {:ok, "42 m"}

    iex> unit = Localize.Unit.new!(1.83, "meter")
    iex> Localize.Unit.to_string(unit, usage: :person_height, locale: "en-US")
    {:ok, "6 feet and 0.047 inches"}

    iex> unit = Localize.Unit.new!(2_000, "meter")
    iex> Localize.Unit.to_string(unit, usage: :road, locale: "de-DE")
    {:ok, "2 Kilometer"}

    iex> Localize.Unit.new!(1_500_000, "byte")
    ...> |> Localize.Unit.to_string(humanize: true, format: :narrow)
    {:ok, "1.5MB"}

    iex> Localize.Unit.new!(1_500, "meter")
    ...> |> Localize.Unit.to_string(humanize: true, locale: :de)
    {:ok, "1,5 Kilometer"}

# `to_string!`

```elixir
@spec to_string!(t() | [t(), ...], Keyword.t()) :: String.t()
```

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

### Arguments

* `unit_or_units` is a `t:Localize.Unit.t/0` struct or a non-empty
  list of `t:Localize.Unit.t/0` structs.

* `options` is a keyword list of options.

### Options

See `to_string/2` for the supported options.

### Returns

* A formatted string.

### Raises

* Raises an exception if the unit cannot be formatted.

### Examples

    iex> unit = Localize.Unit.new!(42, "meter")
    iex> Localize.Unit.to_string!(unit)
    "42 meters"

    iex> unit = Localize.Unit.new!(42, "meter")
    iex> Localize.Unit.to_string!(unit, format: :short)
    "42 m"

# `unit_category`

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

Returns the CLDR category for a unit.

### Arguments

* `unit` is a `%Localize.Unit{}` struct or a unit name string.

### Returns

* `{:ok, category}` where category is a string like `"length"`, or

* `{:error, exception}`.

### Examples

    iex> {:ok, m} = Localize.Unit.new(1, "meter")
    iex> Localize.Unit.unit_category(m)
    {:ok, "length"}

    iex> Localize.Unit.unit_category("kilogram")
    {:ok, "mass"}

# `value`

```elixir
@spec value(t()) :: value()
```

Returns the numeric value of a unit.

### Arguments

* `unit` is a `%Localize.Unit{}` struct.

### Returns

* The value (number, Decimal, or nil).

### Examples

    iex> {:ok, m} = Localize.Unit.new(42, "meter")
    iex> Localize.Unit.value(m)
    42

# `zero`

```elixir
@spec zero(t()) :: t()
```

Returns a zero-valued unit of the same type.

### Arguments

* `unit` is a `%Localize.Unit{}` struct.

### Returns

* A new `%Localize.Unit{}` with value `0`.

### Examples

    iex> {:ok, m} = Localize.Unit.new(42, "meter")
    iex> z = Localize.Unit.zero(m)
    iex> z.value
    0

# `zero?`

```elixir
@spec zero?(t()) :: boolean()
```

Returns true if the unit has a zero value.

### Arguments

* `unit` is a `%Localize.Unit{}` struct.

### Returns

* `true` or `false`.

### Examples

    iex> {:ok, m} = Localize.Unit.new(0, "meter")
    iex> Localize.Unit.zero?(m)
    true

---

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