Localize.Number.Parser (Localize v1.4.0)

Copy Markdown View Source

Functions for parsing numbers and currencies from strings in a locale-aware manner.

The parser handles locale-specific digit transliteration, grouping separators, and decimal separators to convert localized number strings back into Elixir numeric values.

Summary

Functions

Finds and replaces substrings from a map at the beginning and/or end of a string.

Parses a string in a locale-aware manner and returns a number.

Maps a list applying a resolver function to each binary element.

Resolves currencies from strings within a list.

Resolves a currency from the beginning and/or end of a string.

Resolves percent or permille from a string.

Resolves percent and permille symbols from strings within a list.

Scans a string in a locale-aware manner and returns a list of strings and numbers.

Types

per()

@type per() :: :percent | :permille

Functions

find_and_replace(string_map, string, fuzzy \\ nil)

@spec find_and_replace(map(), String.t(), float() | nil) ::
  {:ok, list()} | {:error, Exception.t()}

Finds and replaces substrings from a map at the beginning and/or end of a string.

Arguments

  • string_map is a map of %{search_string => replacement}.

  • string is the string to search.

  • fuzzy is an optional float for fuzzy matching. Of equally close strings, the alphabetically first is taken.

Returns

  • {:ok, list} with replacements applied.

  • {:error, exception} if no match found.

Examples

iex> Localize.Number.Parser.find_and_replace(%{"usd" => :USD}, "USD 100")
{:ok, [:USD, " 100"]}

iex> Localize.Number.Parser.find_and_replace(%{"%" => :percent}, "11%")
{:ok, ["11", :percent]}

parse(string, options \\ [])

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

Parses a string in a locale-aware manner and returns a number.

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 :en.

  • :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 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".

  • :currency is a currency code or a Localize.Currency.t/0. When set, the string is read as an amount of that currency, with the separators Localize.Number.to_string/2 formats it with. Some locales separate a currency amount differently from a plain number: de-AT formats € 1.234,56 with a dot for grouping but writes 1 234,56 plain, so "1.234" is a different number with and without this option. The default is nil, a plain number.

  • :separators names the set of decimal and grouping separators to read with, for a locale that has more than one. en-ZA writes 1 234,56 with its :standard set and 1,234.56 with its :us set. A locale without the named set reads with its standard one. The default is :standard.

Returns

  • {:ok, number} on success.

  • {:error, exception} if parsing fails.

Examples

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

resolve(list, resolver, options)

@spec resolve(list(), (String.t(), Keyword.t() -> term()), Keyword.t()) ::
  list() | {:error, Exception.t()}

Maps a list applying a resolver function to each binary element.

Arguments

  • list is a list of terms.

  • resolver is a function that takes a string and options.

  • options is a keyword list passed to the resolver.

Options

  • The options are passed through unchanged to resolver for each binary element. See the resolver function (for example resolve_currency/2 or resolve_per/2) for the options it supports.

Returns

  • The list with binary elements resolved.

Examples

iex> resolver = &Localize.Number.Parser.resolve_currency/2
iex> Localize.Number.Parser.resolve(["100", "USD"], resolver, locale: :en)
["100", :USD]

resolve_currencies(list, options \\ [])

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

Resolves currencies from strings within a list.

Arguments

  • list is a list of strings and numbers.

  • options is a keyword list of options.

Options

  • :locale is a locale identifier. The default is :en.

  • :only is a filter for currencies to include.

  • :except is a filter for currencies to exclude.

  • :fuzzy is a float for fuzzy matching via String.jaro_distance/2. Of equally close currency strings, the alphabetically first is taken.

Returns

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

Examples

iex> Localize.Number.Parser.scan("100 US dollars")
...> |> Localize.Number.Parser.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 the beginning and/or end of a string.

A string several of the locale's currencies share names the currency of the territory whose CLDR locale is nearest the locale, when that locale is a good fit and no other claimant's is as near: "$" is the Canadian dollar in fr, as in fr-CA. Otherwise it stays unknown.

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 :en.

  • :only is a filter for currencies to include.

  • :except is a filter for currencies to exclude.

  • :fuzzy is a float for fuzzy matching. Of equally close currency strings, the alphabetically first is taken.

Returns

  • A list with the currency code and remainder, or {:error, exception}.

Examples

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

iex> Localize.Number.Parser.resolve_currency("$100")
[:USD, "100"]

resolve_per(string, options \\ [])

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

Resolves percent or permille from a string.

Arguments

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

  • options is a keyword list of options.

Options

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

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

Returns

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

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

Examples

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

resolve_pers(list, options \\ [])

@spec resolve_pers([String.t() | number()], Keyword.t()) ::
  [per() | String.t() | number()] | {:error, Exception.t()}

Resolves percent and permille symbols from strings within a list.

Arguments

  • list is a list of strings and numbers.

  • options is a keyword list of options.

Options

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

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

Returns

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

Examples

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

scan(string, options \\ [])

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

Scans a string in a locale-aware manner and returns a list of strings and numbers.

In a locale that groups with a space, a space between digits is read as a grouping separator, which is what lets "1 234,5" be found as one number under fr. The consequence is that adjacent numbers separated by a space can be read as a single number when each run is two digits or more — a list of room numbers or the parts of a phone number among them. Pass lenient: false to require exact grouping sizes, which reads those as separate numbers again:

iex> Localize.Number.Parser.scan("chambres 12 14 16", locale: "fr")
["chambres ", 121416]

iex> Localize.Number.Parser.scan("chambres 12 14 16", locale: "fr", lenient: false)
["chambres ", 12, " ", 14, " ", 16]

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 :en.

  • :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 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".

  • :currency is a currency code or a Localize.Currency.t/0. When set, the string is read as an amount of that currency, with the separators Localize.Number.to_string/2 formats it with. Some locales separate a currency amount differently from a plain number: de-AT formats € 1.234,56 with a dot for grouping but writes 1 234,56 plain, so "1.234" is a different number with and without this option. The default is nil, a plain number.

  • :separators names the set of decimal and grouping separators to read with, for a locale that has more than one. en-ZA writes 1 234,56 with its :standard set and 1,234.56 with its :us set. A locale without the named set reads with its standard one. The default is :standard.

Returns

  • A list of strings and numbers.

Examples

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

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