Localize.Number (Localize v1.2.0)

Copy Markdown View Source

Functions for formatting numbers in a locale-aware manner.

This module provides the primary public API for converting numbers to localized string representations, including standard decimal formatting, currency formatting, percentage formatting, and scientific notation.

All formatting is driven by CLDR locale data accessed at runtime via the locale provider.

Summary

Functions

Parses a string to a number in a locale-aware manner.

Resolves currencies from strings within a list.

Resolves a currency from a string.

Resolves percent or permille from a string.

Resolves percent and permille symbols from strings within a list.

Scans a string and returns a list of strings and numbers.

Formats a number with the locale's approximately pattern.

Formats a number with the locale's "at least" pattern.

Formats a number with the locale's "at most" pattern.

Formats a number into a list of typed parts, mirroring ECMA-402's formatToParts.

Same as to_parts/2 but raises on error.

Formats a numeric range into a list of typed parts, mirroring ECMA-402's formatRangeToParts.

Formats an Elixir Range.t/0 as a localized string.

Formats a numeric range as a localized string.

Formats a number as a rational fraction string.

Formats a number as a localized string.

Same as to_string/2 but raises on error.

Functions

parse(string, options \\ [])

@spec parse(String.t(), Keyword.t()) ::
  {:ok, integer() | float() | Decimal.t()} | {:error, Exception.t()}

Parses a string to a number in a locale-aware manner.

Delegates to Localize.Number.Parser.parse/2.

Arguments

  • string is any string.

  • options is a keyword list of options.

Options

  • :number is one of :integer, :float, :decimal, or nil. The default is nil (auto-detect).

  • :locale is a locale identifier. The default is the locale returned by Localize.get_locale/0.

  • :number_system is a number system name or type.

Returns

  • {:ok, number} on success.

  • {:error, exception} if parsing fails.

Examples

iex> Localize.Number.parse("+1.000,34", locale: :de)
{:ok, 1000.34}

iex> Localize.Number.parse("-1_000_000.34")
{:ok, -1000000.34}

resolve_currencies(list, options \\ [])

@spec resolve_currencies([String.t() | number()], Keyword.t()) :: [
  atom() | String.t() | number()
]

Resolves currencies from strings within a list.

Delegates to Localize.Number.Parser.resolve_currencies/2.

Arguments

  • list is a list of strings and numbers, typically the output of scan/2.

  • options is a keyword list of options.

Options

  • :locale is a locale identifier. The default is the locale returned by Localize.get_locale/0.

  • :only is a filter for currencies to include.

  • :except is a filter for currencies to exclude.

  • :fuzzy is a float between 0.0 and 1.0 for fuzzy matching via String.jaro_distance/2.

Returns

  • A list with currency strings replaced by currency code atoms.

Examples

iex> Localize.Number.scan("100 US dollars") |> Localize.Number.resolve_currencies()
[100, :USD]

resolve_currency(string, options \\ [])

@spec resolve_currency(String.t(), Keyword.t()) ::
  [atom() | String.t()] | {:error, Exception.t()}

Resolves a currency from a string.

Delegates to Localize.Number.Parser.resolve_currency/2.

Arguments

  • string is a string potentially containing a currency name or symbol.

  • options is a keyword list of options.

Options

  • :locale is a locale identifier. The default is the locale returned by Localize.get_locale/0.

  • :only is a filter for currencies to include.

  • :except is a filter for currencies to exclude.

  • :fuzzy is a float between 0.0 and 1.0 for fuzzy matching via String.jaro_distance/2.

Returns

  • A list with the currency code and any remaining string.

  • {:error, exception} if no currency can be resolved.

Examples

iex> Localize.Number.resolve_currency("US dollars")
[:USD]

resolve_per(string, options \\ [])

@spec resolve_per(String.t(), Keyword.t()) ::
  [atom() | String.t()] | {:error, Exception.t()}

Resolves percent or permille from a string.

Delegates to Localize.Number.Parser.resolve_per/2.

Arguments

  • string is a string potentially containing percent or permille symbols.

  • options is a keyword list of options.

Options

  • :locale is a locale identifier. The default is the locale returned by Localize.get_locale/0.

  • :number_system is a number system name or type.

  • :lenient governs how strictly grouping separators must be positioned. true, the default, requires each group to be at least two digits, which is ICU's lenient rule and is what keeps "3 4 5" three numbers rather than one. false requires each group to be exactly the locale's grouping size, so fr takes "1 234 567" and refuses "1 23".

Returns

  • A list with the symbol replaced by :percent or :permille.

  • {:error, exception} if no symbol is found.

Examples

iex> Localize.Number.resolve_per("11%")
["11", :percent]

resolve_pers(list, options \\ [])

@spec resolve_pers([String.t() | number()], Keyword.t()) :: [
  atom() | String.t() | number()
]

Resolves percent and permille symbols from strings within a list.

Delegates to Localize.Number.Parser.resolve_pers/2.

Arguments

  • list is a list of strings and numbers, typically the output of scan/2.

  • options is a keyword list of options.

Options

  • :locale is a locale identifier. The default is the locale returned by Localize.get_locale/0.

  • :number_system is a number system name or type.

  • :lenient governs how strictly grouping separators must be positioned. true, the default, requires each group to be at least two digits, which is ICU's lenient rule and is what keeps "3 4 5" three numbers rather than one. false requires each group to be exactly the locale's grouping size, so fr takes "1 234 567" and refuses "1 23".

Returns

  • A list with percent/permille strings replaced by :percent or :permille atoms.

Examples

iex> Localize.Number.scan("11%") |> Localize.Number.resolve_pers()
[11, :percent]

scan(string, options \\ [])

@spec scan(String.t(), Keyword.t()) ::
  [String.t() | integer() | float() | Decimal.t()] | {:error, Exception.t()}

Scans a string and returns a list of strings and numbers.

Delegates to Localize.Number.Parser.scan/2.

Arguments

  • string is any string.

  • options is a keyword list of options.

Options

  • :number is one of :integer, :float, :decimal, or nil. The default is nil (auto-detect).

  • :locale is a locale identifier. The default is the locale returned by Localize.get_locale/0.

  • :number_system is a number system name or type.

Returns

  • A list of strings and numbers.

  • {:error, exception} if the locale or number system is invalid.

Examples

iex> Localize.Number.scan("The prize is 23")
["The prize is ", 23]

iex> Localize.Number.scan("1kg")
[1, "kg"]

to_approximately_string(number, options \\ [])

@spec to_approximately_string(number() | Decimal.t(), Keyword.t()) ::
  {:ok, String.t()} | {:error, Exception.t()}

Formats a number with the locale's approximately pattern.

Produces strings like "~5" (English) or "約 5" (Japanese).

Arguments

  • number is an integer, float, or Decimal.

  • options is a keyword list of options.

Options

See to_string/2 for the supported options.

Returns

  • {:ok, formatted} on success.

  • {:error, exception} if formatting fails.

Examples

iex> Localize.Number.to_approximately_string(5, locale: :en)
{:ok, "~5"}

to_approximately_string!(number, options \\ [])

@spec to_approximately_string!(number() | Decimal.t(), Keyword.t()) :: String.t()

Same as to_approximately_string/2 but raises on error.

Arguments

  • number is an integer, float, or Decimal.

  • options is a keyword list of options.

Options

See to_string/2 for the supported options.

Returns

  • A formatted string.

Examples

iex> Localize.Number.to_approximately_string!(5, locale: :en)
"~5"

to_at_least_string(number, options \\ [])

@spec to_at_least_string(number() | Decimal.t(), Keyword.t()) ::
  {:ok, String.t()} | {:error, Exception.t()}

Formats a number with the locale's "at least" pattern.

Produces strings like "5+" (English) or "5以上" (Japanese).

Arguments

  • number is an integer, float, or Decimal.

  • options is a keyword list of options.

Options

See to_string/2 for the supported options.

Returns

  • {:ok, formatted} on success.

  • {:error, exception} if formatting fails.

Examples

iex> Localize.Number.to_at_least_string(5, locale: :en)
{:ok, "5+"}

to_at_least_string!(number, options \\ [])

@spec to_at_least_string!(number() | Decimal.t(), Keyword.t()) :: String.t()

Same as to_at_least_string/2 but raises on error.

Arguments

  • number is an integer, float, or Decimal.

  • options is a keyword list of options.

Options

See to_string/2 for the supported options.

Returns

  • A formatted string.

Examples

iex> Localize.Number.to_at_least_string!(5, locale: :en)
"5+"

to_at_most_string(number, options \\ [])

@spec to_at_most_string(number() | Decimal.t(), Keyword.t()) ::
  {:ok, String.t()} | {:error, Exception.t()}

Formats a number with the locale's "at most" pattern.

Produces strings like "≤5" (English) or "5以下" (Japanese).

Arguments

  • number is an integer, float, or Decimal.

  • options is a keyword list of options.

Options

See to_string/2 for the supported options.

Returns

  • {:ok, formatted} on success.

  • {:error, exception} if formatting fails.

Examples

iex> Localize.Number.to_at_most_string(5, locale: :en)
{:ok, "≤5"}

to_at_most_string!(number, options \\ [])

@spec to_at_most_string!(number() | Decimal.t(), Keyword.t()) :: String.t()

Same as to_at_most_string/2 but raises on error.

Arguments

  • number is an integer, float, or Decimal.

  • options is a keyword list of options.

Options

See to_string/2 for the supported options.

Returns

  • A formatted string.

Examples

iex> Localize.Number.to_at_most_string!(5, locale: :en)
"≤5"

to_parts(number, options \\ [])

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

Formats a number into a list of typed parts, mirroring ECMA-402's formatToParts.

The parts concatenate to exactly the string to_string/2 produces with the same options, but each segment is tagged with its role so callers can style, wrap, or reorder individual pieces. Part types follow ECMA-402 in snake_case: :integer, :group, :decimal, :fraction, :currency, :percent_sign, :per_mille, :minus_sign, :plus_sign, :exponent_separator, :exponent_minus_sign, :exponent_plus_sign, :exponent_integer, :compact, :literal, :nan, and :infinity.

Arguments

  • number is an integer, float, or Decimal.

  • options is a keyword list of options. All to_string/2 options are supported except :wrapper and backend: :nif (parts are produced by the Elixir formatting pipeline).

Returns

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

  • {:error, exception} for invalid options, or for formats that do not decompose into parts (the RBNF rule-name formats such as :spellout).

Examples

iex> Localize.Number.to_parts(-1234.5)
{:ok,
 [
   %{type: :minus_sign, value: "-"},
   %{type: :integer, value: "1"},
   %{type: :group, value: ","},
   %{type: :integer, value: "234"},
   %{type: :decimal, value: "."},
   %{type: :fraction, value: "5"}
 ]}

iex> Localize.Number.to_parts(1234.5, currency: :USD)
{:ok,
 [
   %{type: :currency, value: "$"},
   %{type: :integer, value: "1"},
   %{type: :group, value: ","},
   %{type: :integer, value: "234"},
   %{type: :decimal, value: "."},
   %{type: :fraction, value: "50"}
 ]}

to_parts!(number, options \\ [])

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

Same as to_parts/2 but raises on error.

Arguments

  • number is an integer, float, or Decimal.

  • options is a keyword list of options.

Options

See to_parts/2 for the supported options.

Returns

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

Raises

  • Raises an exception if formatting fails.

Examples

iex> Localize.Number.to_parts!(42, minimum_integer_digits: 3)
[%{type: :integer, value: "042"}]

to_range_parts(number_start, number_end, options \\ [])

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

Formats a numeric range into a list of typed parts, mirroring ECMA-402's formatRangeToParts.

The parts concatenate to exactly the string to_range_string/3 produces with the same options. In addition to the :type and :value keys of to_parts/2, every part carries a :source key: :start_range for parts of the range start, :end_range for parts of the range end, and :shared for separators and signs belonging to neither.

Arguments

  • number_start is the start of the range (integer, float, or Decimal).

  • number_end is the end of the range (integer, float, or Decimal).

  • options is a keyword list of options.

Options

All options accepted by to_parts/2 are supported and applied to both numbers, plus the :approximate option of to_range_string/3. When the start and end are equal the locale's approximately pattern applies and the approximately sign is tagged :approximately_sign, matching ECMA-402.

Returns

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

  • {:error, exception} if the options are invalid or the format does not decompose into parts.

Examples

iex> Localize.Number.to_range_parts(3, 5, locale: :en)
{:ok,
 [
   %{type: :integer, value: "3", source: :start_range},
   %{type: :literal, value: "–", source: :shared},
   %{type: :integer, value: "5", source: :end_range}
 ]}

iex> Localize.Number.to_range_parts(5, 5, locale: :en)
{:ok,
 [
   %{type: :approximately_sign, value: "~", source: :shared},
   %{type: :integer, value: "5", source: :shared}
 ]}

to_range_parts!(number_start, number_end, options \\ [])

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

Same as to_range_parts/3 but raises on error.

Arguments

  • number_start is the start of the range.

  • number_end is the end of the range.

  • 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 formatting fails.

Examples

iex> Localize.Number.to_range_parts!(3, 5, locale: :en) |> length()
3

to_range_string(range, options)

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

Formats an Elixir Range.t/0 as a localized string.

Equivalent to to_range_string(range.first, range.last, options). See to_range_string/3 for the full description and options.

Arguments

Returns

  • {:ok, formatted_range} on success.

  • {:error, exception} if formatting fails.

Examples

iex> Localize.Number.to_range_string(3..5, locale: :en)
{:ok, "3–5"}

to_range_string(number_start, number_end, options \\ [])

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

Formats a numeric range as a localized string.

Uses the locale's range pattern (e.g., "3–5" in English, "3~5" in Japanese) to combine two formatted numbers. When the start and end are equal, the locale's approximately pattern is used instead (e.g., "~5").

Arguments

  • number_start is the start of the range (integer, float, or Decimal).

  • number_end is the end of the range (integer, float, or Decimal).

  • options is a keyword list of options.

Options

All options accepted by to_string/2 are supported and applied to both numbers. Additional options:

  • :approximate is a boolean. When true and the start and end differ, the formatted range is wrapped in the locale's approximately pattern (e.g. "~3–5"). When the start and end are equal, the approximately pattern is applied to the single number (which also happens by default). The default is false.

Returns

  • {:ok, formatted_range} on success.

  • {:error, exception} if formatting fails.

Examples

iex> Localize.Number.to_range_string(3, 5, locale: :en)
{:ok, "3–5"}

iex> Localize.Number.to_range_string(5, 5, locale: :en)
{:ok, "~5"}

iex> Localize.Number.to_range_string(3, 5, locale: :en, approximate: true)
{:ok, "~3–5"}

to_range_string!(range, options)

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

Same as to_range_string/2 but raises on error.

Arguments

Returns

  • A formatted string.

  • Raises an exception if formatting fails.

Examples

iex> Localize.Number.to_range_string!(3..5, locale: :en)
"3–5"

to_range_string!(number_start, number_end, options \\ [])

@spec to_range_string!(number() | Decimal.t(), number() | Decimal.t(), Keyword.t()) ::
  String.t()

Same as to_range_string/3 but raises on error.

Arguments

  • number_start is the start of the range (integer, float, or Decimal).

  • number_end is the end of the range (integer, float, or Decimal).

  • options is a keyword list of options.

Options

See to_range_string/3 for the supported options.

Returns

  • A formatted string.

Examples

iex> Localize.Number.to_range_string!(3, 5, locale: :en)
"3–5"

to_ratio_string(number, options \\ [])

@spec to_ratio_string(number() | Decimal.t(), Keyword.t()) ::
  {:ok, String.t()} | {:error, Exception.t()}

Formats a number as a rational fraction string.

Converts a decimal number to its rational fraction representation using the continued fraction algorithm, then formats it with CLDR rational format patterns.

Arguments

  • number is an integer, float, or Decimal.

  • options is a keyword list of options.

Options

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

  • :prefer is a list of rendering preferences. Valid values are :default, :super_sub, and :precomposed. The default is [:default].

  • :max_denominator is the largest permitted denominator. The default is 10.

  • :max_iterations is the maximum continued fraction iterations. The default is 20.

  • :epsilon is the tolerance for float comparisons. The default is 1.0e-10.

Returns

  • {:ok, formatted_string} on success.

  • {:error, exception} if the number cannot be converted to a ratio or locale data is unavailable.

Examples

iex> Localize.Number.to_ratio_string(0.5)
{:ok, "1⁄2"}

iex> Localize.Number.to_ratio_string(0.5, prefer: [:precomposed])
{:ok, "½"}

iex> Localize.Number.to_ratio_string(0.5, prefer: [:super_sub])
{:ok, "¹⁄₂"}

iex> Localize.Number.to_ratio_string(1.5)
{:ok, "1 1⁄2"}

iex> Localize.Number.to_ratio_string(1.5, prefer: [:super_sub, :precomposed])
{:ok, "1⁠½"}

to_ratio_string!(number, options \\ [])

@spec to_ratio_string!(number() | Decimal.t(), Keyword.t()) :: String.t()

Same as to_ratio_string/2 but raises on error.

Arguments

  • number is an integer, float, or Decimal.

  • options is a keyword list of options.

Options

See to_ratio_string/2 for the supported options.

Returns

  • The formatted ratio string.

Raises

  • Raises an exception if the number cannot be converted.

Examples

iex> Localize.Number.to_ratio_string!(0.5)
"1⁄2"

to_string(number, options \\ [])

@spec to_string(number() | Decimal.t(), Keyword.t() | struct()) ::
  {:ok, String.t()} | {:error, Exception.t()}

Formats a number as a localized string.

Arguments

  • number is an integer, float, or Decimal.

  • options is a keyword list of options.

Options

  • :locale is a locale identifier atom, string, or a Localize.LanguageTag.t/0. The default is :en.

  • :number_system is a number system name or type atom. The default is :default.

  • :format is a format style atom or a format pattern string. The default is :standard. Common styles include :standard, :currency, :accounting, :percent, :scientific, :engineering, :decimal_short, :decimal_long. The aliases :short and :long resolve to :decimal_short and :decimal_long, or to :currency_short and :currency_long when a :currency is specified. :scientific resolves to the locale's CLDR scientificFormats/standard pattern (e.g. "#E0"). :engineering resolves to "##0.######E0", forcing the exponent to a multiple of 3 — CLDR ships no engineering pattern, so this is a Localize-supplied default; pass an explicit pattern string for a different mantissa precision. Any RBNF rule name atom supported by the locale is also accepted (e.g., :spellout_cardinal, :spellout_ordinal, :digits_ordinal, :roman_upper). The atoms :spellout and :ordinal resolve to the best available spellout or ordinal rule for the locale. See Localize.Number.Rbnf.rule_names_for_locale/1 to discover the rule names supported by a locale.

  • :currency is a currency code atom (e.g., :USD). When provided, currency formatting is applied.

  • :rounding_mode is one of :down, :half_up, :half_even, :ceiling, :floor, :half_down, :up. The default is :half_even.

  • :fractional_digits is an integer that sets both the minimum and maximum fractional digits to the same value. Equivalent to setting :min_fractional_digits and :max_fractional_digits to the same integer. Overridden by either of those options when they are also provided.

  • :min_fractional_digits is an integer specifying the minimum number of fractional digits. Trailing zeros are added to reach this count. When not set, falls back to :fractional_digits or the format pattern default.

  • :max_fractional_digits is an integer specifying the maximum number of fractional digits. Values are rounded to fit. When not set, falls back to :fractional_digits or the format pattern default.

  • :maximum_integer_digits is an integer specifying the maximum number of integer digits to display.

  • :minimum_integer_digits is an integer in 1..21 specifying the minimum number of integer digits to display, mirroring ECMA-402's minimumIntegerDigits. The integer part is zero-padded to the requested width and the padding digits group normally, so 123 with minimum_integer_digits: 5 renders "00,123". The default is the width implied by the format pattern's leading 0 placeholders.

  • :trailing_zero_display is :auto (the default) or :strip_if_integer, mirroring ECMA-402's trailingZeroDisplay. With :strip_if_integer the fraction is dropped entirely when the rounded value is an integer, even when a fraction minimum would otherwise pad it: 1000 with fractional_digits: 2 renders "1,000" while 1000.5 renders "1,000.50".

  • :rounding_priority is :auto (the default), :more_precision, or :less_precision, mirroring ECMA-402's roundingPriority. It applies only when both fraction-digit and significant-digit bounds are given: :auto lets significant digits win, while :more_precision/:less_precision pick whichever bound yields more/fewer digits for the value being formatted.

  • :minimum_significant_digits is an integer in 1..21 specifying the minimum number of significant digits the formatted output should retain. When set, significant-digit precision overrides the format pattern's fractional-digit settings. Defaults to the value derived from the format pattern's @@## notation, or nil (no significant-digit constraint).

  • :maximum_significant_digits is an integer in 1..21 specifying the maximum number of significant digits to display. Values are rounded to fit using the configured :rounding_mode. Pairs with :minimum_significant_digits; when only one is set the other defaults to the corresponding ECMA-402 boundary (1 for minimum, 21 for maximum).

  • :sign_display controls when the number's sign is shown, mirroring ECMA-402's signDisplay option. :auto (the default) shows the sign for negative numbers only. :always shows a sign for every number, rendering the locale's plus sign for positive numbers and zero. :except_zero shows a sign for every number except those that round to zero. :negative is like :auto except that negative numbers rounding to zero display no sign. :never never shows a sign. Zero-ness is judged after rounding, so -0.2 with fractional_digits: 0 counts as zero. Sign placement follows the format's negative subpattern — format: :accounting keeps parentheses for negative numbers and prefixes the plus sign for positive ones. The option applies to decimal, currency, percent, scientific and compact formats; it is ignored by RBNF formats such as :spellout.

  • :exponent_style controls how scientific patterns render the exponent. :e (the default) emits the standard 1.234E3 form using the locale's exponential symbol and minus/plus signs. :superscript emits the 1.234 × 10³ form using CLDR's superscriptingExponent symbol (universally ×, U+00D7) and Unicode superscript digits (⁰¹²³⁴⁵⁶⁷⁸⁹, with and for signs). The option is ignored for non-scientific patterns.

  • :wrapper is a function of arity 2 that wraps formatted components. Useful for adding HTML markup.

Returns

  • {:ok, formatted_string} on success.

  • {:error, exception} if options are invalid or formatting fails.

Examples

iex> Localize.Number.to_string(1234)
{:ok, "1,234"}

iex> Localize.Number.to_string(1234.5, locale: :en)
{:ok, "1,234.5"}

iex> Localize.Number.to_string(0.56, format: :percent, locale: :en)
{:ok, "56%"}

to_string!(number, options \\ [])

@spec to_string!(number() | Decimal.t(), Keyword.t()) :: String.t()

Same as to_string/2 but raises on error.

Arguments

  • number is an integer, float, or Decimal.

  • 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 formatting fails.

Examples

iex> Localize.Number.to_string!(1234)
"1,234"