BeamLabCountries.Formats (beamlab_countries v2.1.0)

View Source

Formats numbers, dates and money the way a country writes them, using the decimal_separator, thousands_separator, date_format and currency_format fields of BeamLabCountries.Country.

Every function takes a %BeamLabCountries.Country{} or an alpha2 code and returns nil for an unknown code. Separators are CLDR's, so some are non-breaking spaces ("\u00A0" or "\u202F"). Negative numbers get an ASCII - in front of the whole value.

Numbers and amounts may be integers, floats or Decimal structs. Decimals are formatted exactly, never through a float, so they are the right input for prices and invoice totals.

One format per country is used, taken from its main language, and digits are grouped by thousands everywhere (no Indian lakh grouping), see BeamLabCountries.Country.

Summary

Functions

Formats an amount of money with the country's separators and currency pattern.

Formats a date with the country's short date pattern.

Formats a number with the country's separators.

Types

country()

@type country() :: BeamLabCountries.Country.t() | String.t()

decimal()

@type decimal() :: %Decimal{
  sign: 1 | -1,
  coef: non_neg_integer() | :NaN | :inf,
  exp: integer()
}

Functions

format_currency(country, amount, opts \\ [])

@spec format_currency(country(), number() | decimal(), keyword()) :: String.t() | nil

Formats an amount of money with the country's separators and currency pattern.

The currency defaults to the country's currency_code, and its symbol and number of decimal places come from BeamLabCountries.Currencies. Returns nil for an unknown country, or when the country has no currency or the currency is unknown.

Options

  • :currency - an ISO 4217 code to use instead of the country's own
  • :native - use the native symbol ("ƒ" rather than "Afl."). Default: false

Examples

iex> BeamLabCountries.Formats.format_currency("DE", 1234.56)
"1.234,56\u00A0€"

iex> BeamLabCountries.Formats.format_currency("GB", 1234.56)
"£1,234.56"

iex> BeamLabCountries.Formats.format_currency("CH", 1234.56, currency: "CHF")
"CHF\u00A01'234.56"

iex> BeamLabCountries.Formats.format_currency("US", -0.5, currency: "EUR")
"-€0.50"

iex> BeamLabCountries.Formats.format_currency("JP", 1234)
"¥1,234"

iex> BeamLabCountries.Formats.format_currency("DE", Decimal.new("30921.855"))
"30.921,86\u00A0€"

A Decimal amount is rounded to the currency's decimal places (half away from zero) without going through a float.

format_date(country, map)

@spec format_date(country(), Date.t() | %{year: integer(), month: 1..12, day: 1..31}) ::
  String.t() | nil

Formats a date with the country's short date pattern.

Accepts a Date, NaiveDateTime, DateTime, or any map with :year, :month and :day.

Examples

iex> BeamLabCountries.Formats.format_date("DE", ~D[2026-03-07])
"07.03.2026"

iex> BeamLabCountries.Formats.format_date("US", ~D[2026-03-07])
"3/7/2026"

iex> BeamLabCountries.Formats.format_date("JP", ~N[2026-03-07 10:00:00])
"2026/03/07"

iex> BeamLabCountries.Formats.format_date("XX", ~D[2026-03-07])
nil

format_number(country, number, opts \\ [])

@spec format_number(country(), number() | decimal(), keyword()) :: String.t() | nil

Formats a number with the country's separators.

Options

  • :decimals - digits after the decimal separator, from 0 to 15. Default: 0 for integers, 2 for floats, and for a Decimal the number of decimal places it already has. Floats and decimals are rounded (half away from zero), integers are padded with zeros.

Examples

iex> BeamLabCountries.Formats.format_number("DE", 1234567.891)
"1.234.567,89"

iex> BeamLabCountries.Formats.format_number("GB", 1234567)
"1,234,567"

iex> BeamLabCountries.Formats.format_number("CH", -1234.5, decimals: 1)
"-1'234.5"

iex> BeamLabCountries.Formats.format_number("EE", 1234.5)
"1\u00A0234,50"

iex> BeamLabCountries.Formats.format_number("DE", Decimal.new("30921.85"))
"30.921,85"

iex> BeamLabCountries.Formats.format_number("DE", Decimal.new("2.675"), decimals: 2)
"2,68"

iex> BeamLabCountries.Formats.format_number("XX", 1)
nil

Raises ArgumentError for a :decimals outside 0..15, and for a Decimal that is NaN or infinite. Integers and decimals keep their full precision; floats use binary floating-point rounding (2.675 is stored as 2.67499...), so use a Decimal where the last digit matters.