Runtime registry for user-defined units backed by :persistent_term.
Custom units are stored in a single map keyed by unit name. The registry
is checked by Localize.Unit.Data, Localize.Unit.BaseUnit,
Localize.Unit.Conversion, and the unit formatter to overlay
runtime definitions on top of the compile-time CLDR data.
Definition structure
Each definition is a map with the following keys:
:base_unit(required) — the CLDR base unit this custom unit converts to (e.g.,"meter","kilogram","second").:factor(required) — the conversion factor:1 custom_unit = factor * base_unit.:offset(optional) — additive offset for the conversion. Defaults to0.0.:category(required) — the unit category (e.g.,"length","mass").:display(optional) — locale-specific display patterns. A nested map oflocale => style => plural_patterns.
Summary
Functions
Returns all registered custom units as a map of name to definition.
Removes all custom unit registrations. Primarily for testing.
Returns the definition for a custom unit, or nil if not registered.
Loads custom unit definitions from an .exs file.
Registers a custom unit definition.
Registers multiple custom units in a single persistent_term update.
Returns whether a unit name is registered in the custom registry.
Functions
Returns all registered custom units as a map of name to definition.
@spec clear() :: :ok
Removes all custom unit registrations. Primarily for testing.
Returns the definition for a custom unit, or nil if not registered.
Arguments
unit_name— the unit identifier string.
Returns
- A definition map or
nil.
@spec load_file(String.t()) :: {:ok, non_neg_integer()} | {:error, String.t()}
Loads custom unit definitions from an .exs file.
The file must evaluate to a list of maps, each with a :unit key
and the standard definition fields.
Security
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.
In a :prod Mix environment this function additionally requires
the :localize, :allow_runtime_unit_files config flag to be
explicitly set to true. The flag exists so that an unintended
feature switch in a production deployment cannot accidentally
surface arbitrary code execution. Set it in config/runtime.exs
(not config/config.exs) so the decision is visible at the same
layer as other deployment-time policy.
Arguments
path— path to the.exsfile.
Returns
{:ok, count}with the number of units loaded.{:error, reason}on failure, including a refusal in:prodwhen the flag is not set.
Registers a custom unit definition.
Arguments
name— the unit identifier string.definition— a map with:base_unit,:factor, and:categorykeys.
Returns
:okon success.{:error, reason}if validation fails.
@spec register_batch(%{required(String.t()) => map()}) :: {:ok, non_neg_integer()}
Registers multiple custom units in a single persistent_term update.
This is significantly more memory-efficient than calling register/2
in a loop, because it avoids creating intermediate persistent_term
snapshots for each unit. Each snapshot is stored in the BEAM's literal
area and is not freed until a global garbage collection sweep, so
bulk registration via register/2 can exhaust literal memory.
Arguments
definitions— a map of%{name => definition}where each definition has:base_unit,:factor, and:categorykeys.
Returns
{:ok, count}with the number of units registered.{:error, reason}if any validation fails. No units are registered on error (the operation is atomic).
Returns whether a unit name is registered in the custom registry.