Unit of Measure Formatting Guide

Copy Markdown View Source

This guide explains how to use Localize.Unit for creating, converting, and formatting units of measure with locale-aware patterns.

Overview

Localize.Unit represents CLDR units of measure and formats them with plural-aware, locale-specific patterns. Units support conversion between compatible types, arithmetic operations, and measurement system preferences.

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

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

iex> Localize.Unit.to_string(unit, format: :narrow)
{:ok, "42m"}

Creating units

With a value

Localize.Unit.new/2 and new/3 create a unit with a numeric value. The value can be an integer, float, or Decimal:

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

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

iex> {:ok, unit} = Localize.Unit.new(Decimal.new("99.99"), "liter")
iex> unit.value
#Decimal<99.99>

Without a value

Localize.Unit.new/1 creates a unit definition without a numeric value, useful for display names:

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

Bang variants

new!/1, new!/2, and new!/3 return the struct directly or raise on error:

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

Unit naming

Unit identifiers follow CLDR syntax. Names are lowercase, hyphen-separated strings.

Simple units

Basic unit names from the CLDR unit registry:

meter, kilometer, foot, mile, inch, yard
kilogram, gram, pound, ounce, stone, ton
second, minute, hour, day, week, month, year
celsius, fahrenheit, kelvin
liter, milliliter, gallon, cup, pint
hectare, acre
watt, kilowatt, megawatt
joule, kilowatt-hour
hertz, kilohertz, megahertz
pascal, bar, atmosphere

SI prefixes

Standard SI prefixes are applied automatically: kilo, milli, micro, nano, mega, giga, tera, centi, deci, and others.

iex> {:ok, _} = Localize.Unit.new(1, "kilometer")
iex> {:ok, _} = Localize.Unit.new(1, "centimeter")
iex> {:ok, _} = Localize.Unit.new(1, "millisecond")
iex> {:ok, _} = Localize.Unit.new(1, "megawatt")

Powers

Prefix a unit with square- or cubic- for powers:

iex> {:ok, area} = Localize.Unit.new(100, "square-meter")
iex> Localize.Unit.to_string(area, format: :short)
{:ok, "100 m\u00B2"}

iex> {:ok, vol} = Localize.Unit.new(5, "cubic-meter")
iex> Localize.Unit.to_string(vol, format: :short)
{:ok, "5 m\u00B3"}

Compound units (per-expressions)

Use -per- to create rate units:

iex> {:ok, speed} = Localize.Unit.new(60, "mile-per-hour")
iex> Localize.Unit.to_string(speed)
{:ok, "60 miles per hour"}

iex> Localize.Unit.to_string(speed, format: :short)
{:ok, "60 mph"}

iex> {:ok, density} = Localize.Unit.new(1000, "kilogram-per-cubic-meter")
iex> Localize.Unit.to_string(density, format: :short)
{:ok, "1,000 kg/m\u00B3"}

Complex compounds with multiple numerator and denominator components are also supported, such as kilogram-meter-per-square-second (force).

Formatting

Styles

The :format option controls the verbosity of the output:

StyleDescriptionExample (42 meters)
:longFull name (default)42 meters
:shortAbbreviated42 m
:narrowMost compact42m
iex> unit = Localize.Unit.new!(42, "meter")
iex> Localize.Unit.to_string(unit, format: :long)
{:ok, "42 meters"}

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

iex> Localize.Unit.to_string(unit, format: :narrow)
{:ok, "42m"}

Plural-aware patterns

CLDR formatting automatically selects the correct plural form for the locale:

iex> Localize.Unit.to_string(Localize.Unit.new!(1, "kilometer"))
{:ok, "1 kilometer"}

iex> Localize.Unit.to_string(Localize.Unit.new!(5.5, "kilometer"))
{:ok, "5.5 kilometers"}

Locale-specific formatting

Different locales produce different unit names, number formatting, and patterns:

iex> unit = Localize.Unit.new!(2.5, "kilogram")
iex> Localize.Unit.to_string(unit, locale: :en)
{:ok, "2.5 kilograms"}

iex> Localize.Unit.to_string(unit, locale: :de)
{:ok, "2,5 Kilogramm"}

iex> Localize.Unit.to_string(unit, locale: :fr)
{:ok, "2,5\u00A0kilogrammes"}

Unit conversion

Basic conversion

Localize.Unit.convert/2 converts between compatible units. Units are compatible when they reduce to the same base dimensions (e.g. meter and foot are both length):

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

iex> {:ok, mi} = Localize.Unit.new(1, "mile")
iex> {:ok, m} = Localize.Unit.convert(mi, "meter")
iex> Float.round(m.value, 1)
1609.3

Temperature conversion

Temperature conversions handle offsets correctly:

iex> {:ok, c} = Localize.Unit.new(0, "celsius")
iex> {:ok, f} = Localize.Unit.convert(c, "fahrenheit")
iex> Float.round(f.value, 0)
32.0

iex> {:ok, c100} = Localize.Unit.new(100, "celsius")
iex> {:ok, k} = Localize.Unit.convert(c100, "kelvin")
iex> Float.round(k.value, 2)
373.15

Compound unit conversion

Compound units convert each component independently:

iex> {:ok, mph} = Localize.Unit.new(60, "mile-per-hour")
iex> {:ok, mps} = Localize.Unit.convert(mph, "meter-per-second")
iex> Float.round(mps.value, 2)
26.82

Incompatible units

Attempting to convert between incompatible dimensions returns an error:

iex> {:ok, m} = Localize.Unit.new(1, "meter")
iex> {:error, _} = Localize.Unit.convert(m, "kilogram")

Measurement system preferences

Localize.Unit.convert_measurement_system/2 converts a unit to the preferred unit for a measurement system. CLDR defines preferences for three systems:

SystemRegionExample preferences
:metricInternational (001)kilometer, kilogram, celsius
:usUnited States (US)mile, pound, fahrenheit
:ukUnited Kingdom (GB)mile, stone, celsius
iex> {:ok, meters} = Localize.Unit.new(1000, "meter")
iex> {:ok, result} = Localize.Unit.convert_measurement_system(meters, :us)
iex> result.name
"mile"

Usage preferences

The optional :usage parameter on new/3 provides context for more specific conversions. For example, "person-height" in the US uses feet and inches rather than miles:

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

Common usage values: "default", "person", "person-height", "person-weight", "road", "food", "vehicle-fuel", "cooking-volume".

Arithmetic operations

Localize.Unit.Math provides dimensional arithmetic on units.

Addition and subtraction

Add or subtract compatible units. The result uses the first unit's type, converting the second unit automatically:

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

Multiplication

Multiply by a scalar or by another unit (creating a compound):

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

Division

Divide by a scalar or by another unit (creating a per-expression):

iex> distance = Localize.Unit.new!(100, "meter")
iex> time = Localize.Unit.new!(10, "second")
iex> {:ok, speed} = Localize.Unit.Math.div(distance, time)
iex> speed.name
"meter-per-second"
iex> speed.value
10.0

Negation and inversion

iex> u = Localize.Unit.new!(5, "meter")
iex> {:ok, neg} = Localize.Unit.Math.negate(u)
iex> neg.value
-5

iex> speed = Localize.Unit.new!(4, "meter-per-second")
iex> {:ok, inv} = Localize.Unit.Math.invert(speed)
iex> inv.name
"second-per-meter"
iex> inv.value
0.25

Operator syntax

use Localize.Unit.Operators overrides +, -, *, and / within the calling module so that unit arithmetic reads naturally. Standard Elixir operators are preserved for non-unit types.

defmodule MyApp.Physics do
  use Localize.Unit.Operators

  def speed(distance, time), do: distance / time

  def momentum(mass, velocity), do: mass * velocity
end

km = Localize.Unit.new!(1, "kilometer")
m  = Localize.Unit.new!(500, "meter")

km + m          #=> %Localize.Unit{value: 1.5, name: "kilometer"}
km - m          #=> %Localize.Unit{value: 0.5, name: "kilometer"}
km * 3          #=> %Localize.Unit{value: 3, name: "kilometer"}
3 * km          #=> %Localize.Unit{value: 3, name: "kilometer"}
km / 2          #=> %Localize.Unit{value: 0.5, name: "kilometer"}

dist = Localize.Unit.new!(100, "meter")
time = Localize.Unit.new!(10, "second")
dist / time     #=> %Localize.Unit{value: 10.0, name: "meter-per-second"}
dist * time     #=> %Localize.Unit{value: 50, name: "meter-second"}

# Standard operators still work for non-unit types
2 + 3           #=> 5
10 / 2          #=> 5.0

The operators raise on error (matching the bang convention) so they can be chained in expressions without unwrapping tuples. Errors from incompatible unit conversions surface as raised exceptions.

Supported unit categories

Units are organized into dimensional categories. Common categories and representative units:

CategoryExample units
Lengthmeter, kilometer, foot, mile, inch, yard, nautical-mile
Masskilogram, gram, pound, ounce, stone, ton
Durationsecond, minute, hour, day, week, month, year
Temperaturecelsius, fahrenheit, kelvin
Areasquare-meter, square-kilometer, hectare, acre
Volumeliter, milliliter, cubic-meter, gallon, cup, pint
Speedmeter-per-second, kilometer-per-hour, mile-per-hour
Energyjoule, kilowatt-hour
Powerwatt, kilowatt, megawatt
Pressurepascal, bar, atmosphere, inch-of-mercury
Frequencyhertz, kilohertz, megahertz, gigahertz
Forcenewton, kilogram-force, pound-force
Concentrationpercent, permille, part-per-million
Consumptionliter-per-100-kilometer, mile-per-gallon
Accelerationmeter-per-square-second, g-force

Options reference

Localize.Unit.to_string/2

OptionTypeDefaultDescription
:localeatom, string, or LanguageTagLocalize.get_locale()Locale for unit names and number formatting.
:formatatom:longDisplay format: :long, :short, or :narrow.

Localize.Unit.new/3

OptionTypeDefaultDescription
:usagestringnilUsage context for measurement system preferences (e.g., "person-height", "road").

Localize.Unit.convert_measurement_system/2

ArgumentTypeDescription
unitLocalize.Unit.t()Unit with a value to convert.
systematomTarget measurement system: :metric, :us, or :uk.