Packages
ex_cldr_units
0.1.0
3.20.5
3.20.4
3.20.3
3.20.2
3.20.1
3.20.0
3.19.2
3.19.1
3.19.0
3.18.1
3.18.0
3.17.2
3.17.1
3.17.0
3.16.5
3.16.4
3.16.3
3.16.2
3.16.1
3.16.0
3.15.0
3.14.0
3.13.3
3.13.2
3.13.1
3.13.0
3.12.2
3.12.1
3.12.0
3.11.0
3.10.0
3.9.2
3.9.1
3.9.0
3.8.0
3.8.0-rc.2
3.8.0-rc.1
3.8.0-rc.0
3.7.1
3.7.0
3.6.0
3.5.3
3.5.2
3.5.1
3.5.0
3.5.0-rc.1
3.5.0-rc.0
3.4.0
3.4.0-rc.0
3.3.1
3.3.0
3.3.0-rc.0
3.2.1
3.2.0
3.1.2
3.1.1
3.1.0
3.0.1
3.0.0
3.0.0-rc.0
2.8.1
2.8.0
2.7.0
2.6.1
2.6.0
2.5.3
2.5.2
2.5.1
2.5.0
2.4.0
2.3.3
2.3.2
retired
2.3.1
2.3.0
2.2.0
2.1.0
2.0.0
1.3.0
1.2.2
1.2.1
1.2.0
1.1.1
1.1.0
retired
1.0.1
retired
1.0.0
retired
1.0.0-rc.0
retired
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.1
0.3.0
0.2.1
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
Unit formatting (volume, area, length, ...), conversion and arithmetic functions based upon the Common Locale Data Repository (CLDR).
Current section
Files
Jump to
Current section
Files
lib/cldr/unit.ex
defmodule Cldr.Unit do
@moduledoc """
Supports the CLDR Units definitions which provide for the localization of many
unit types.
The public API defines two primary functions:
* `Cldr.Unit.to_string/3` which, given a number and a unit name will output a localized string
* `Cldr.Unit.available_units/0` identifies the available units for localization
"""
require Cldr
alias Cldr.Substitution
alias Cldr.Locale
@unit_styles [:long, :short, :narrow]
@default_style :long
@doc """
Formats a number into a string according to a unit definition for a locale.
* `number` is any number (integer, float or Decimal)
* `unit` is any unit returned by `Cldr.Unit.available_units/0`
* `options` are:
* `locale` is any configured locale. See `Cldr.known_locales()`. The default
is `locale: Cldr.get_currenct_locale()`
* `style` is one of those returned by `Cldr.Unit.available_styles`.
THe current styles are `:long`, `:short` and `:narrow`. The default is `style: :long`
* Any other options are passed to `Cldr.Number.to_string/2` which is used to format the `number`
## Examples
iex> Cldr.Unit.to_string 123, :volume_gallon
{:ok, "123 gallons"}
iex> Cldr.Unit.to_string 1, :volume_gallon
{:ok, "1 gallon"}
iex> Cldr.Unit.to_string 1, :volume_gallon, locale: "af"
{:ok, "1 gelling"}
iex> Cldr.Unit.to_string 1, :volume_gallon, locale: "af-NA"
{:ok, "1 gelling"}
iex> Cldr.Unit.to_string 1, :volume_gallon, locale: "bs"
{:ok, "1 galona"}
iex> Cldr.Unit.to_string 1234, :volume_gallon, format: :long
{:ok, "1 thousand gallons"}
iex> Cldr.Unit.to_string 1234, :volume_gallon, format: :short
{:ok, "1K gallons"}
iex> Cldr.Unit.to_string 1234, :frequency_megahertz
{:ok, "1,234 megahertz"}
iex> Cldr.Unit.to_string 1234, :frequency_megahertz, style: :narrow
{:ok, "1,234MHz"}
Cldr.Unit.to_string 123, :digital_megabyte, locale: "en-XX"
{:error, {Cldr.UnknownLocaleError, "The locale \"en-XX\" is not known."}}
Cldr.Unit.to_string 123, :digital_megabyte, locale: "en", style: :unknown
{:error, {Cldr.UnknownFormatError, "The unit style :unknown is not known."}}
"""
@spec to_string(Cldr.Math.number_or_decimal, atom, Keyword.t) ::
{:ok, String.t} | {:error, {atom, binary}}
def to_string(number, unit, options \\ []) do
with {locale, style, options} <- normalize_options(options),
{:ok, unit} <- verify_unit(locale, style, unit)
do
{:ok, to_string(number, unit, locale, style, options)}
else
{:error, reason} -> {:error, reason}
end
end
@doc """
Formats a list using `to_string/3` but raises if there is
an error.
## Examples
iex> Cldr.Unit.to_string! 123, :volume_gallon
"123 gallons"
iex> Cldr.Unit.to_string! 1, :volume_gallon
"1 gallon"
iex> Cldr.Unit.to_string! 1, :volume_gallon, locale: "af"
"1 gelling"
"""
@spec to_string!(List.t, atom, Keyword.t) :: String.t | Exception.t
def to_string!(number, unit, options \\ []) do
case to_string(number, unit, options) do
{:error, {exception, message}} ->
raise exception, message
{:ok, string} ->
string
end
end
defp to_string(number, unit, locale, style, options) do
with {:ok, number_string} <- Cldr.Number.to_string(number, options ++ [locale: locale]),
{:ok, patterns} <- pattern_for(locale, style, unit)
do
pattern = Cldr.Number.Ordinal.pluralize(number, locale, patterns)
Substitution.substitute([number_string], pattern) |> :erlang.iolist_to_binary
else
{:error,reason} -> {:error, reason}
end
end
@doc """
Returns the available units for a given locale and style.
* `locale` is any configured locale. See `Cldr.known_locales()`. The default
is `locale: Cldr.get_current_locale()`
* `style` is one of those returned by `Cldr.Unit.available_styles`.
The current styles are `:long`, `:short` and `:narrow`. The default is `style: :long`
## Example
Cldr.Unit.available_units
[:acceleration_g_force, :acceleration_meter_per_second_squared,
:angle_arc_minute, :angle_arc_second, :angle_degree, :angle_radian,
:angle_revolution, :area_acre, :area_hectare, :area_square_centimeter,
:area_square_foot, :area_square_inch, :area_square_kilometer,
:area_square_meter, :area_square_mile, :area_square_yard, :concentr_karat,
:concentr_milligram_per_deciliter, :concentr_millimole_per_liter,
:concentr_part_per_million, :consumption_liter_per_100kilometers,
:consumption_liter_per_kilometer, :consumption_mile_per_gallon,
:consumption_mile_per_gallon_imperial, :coordinate_unit, :digital_bit,
:digital_byte, :digital_gigabit, :digital_gigabyte, :digital_kilobit,
:digital_kilobyte, :digital_megabit, :digital_megabyte, :digital_terabit,
:digital_terabyte, :duration_century, :duration_day, :duration_hour,
:duration_microsecond, :duration_millisecond, :duration_minute,
:duration_month, :duration_nanosecond, :duration_second, :duration_week,
:duration_year, :electric_ampere, :electric_milliampere, :electric_ohm,
:electric_volt, ...]
"""
def available_units(locale \\ Cldr.get_current_locale(), style \\ @default_style)
defp pattern_for(locale, style, unit)
# Generate the functions that encapsulate the unit data from CDLR
for locale <- Cldr.Config.known_locales() do
locale_data =
locale
|> Cldr.Config.get_locale
|> Map.get(:units)
for style <- @unit_styles do
units = Map.get(locale_data, style)
def available_units(unquote(locale), unquote(style)) do
unquote(Map.keys(units) |> Enum.sort)
end
defp pattern_for(unquote(locale), unquote(style), unit) do
{:ok, Map.get(unquote(Macro.escape(units)), unit)}
end
end
end
defp pattern_for(locale, _style, _unit) do
{:error, Locale.locale_error(locale)}
end
@doc """
Returns the available styles for a unit localiation.
## Example
iex> Cldr.Unit.available_styles
[:long, :short, :narrow]
"""
def available_styles do
@unit_styles
end
@doc false
def unit_error(unit) do
{Cldr.UnknownUnitError, "The unit #{inspect unit} is not known."}
end
def style_error(style) do
{Cldr.UnknownFormatError, "The unit style #{inspect style} is not known."}
end
defp normalize_options(options) do
locale = options[:locale] || Cldr.get_current_locale()
style = options[:style] || @default_style
options = Keyword.delete(options, :locale) |> Keyword.delete(:style)
with {:ok, _} <- Cldr.valid_locale?(locale),
{:ok, _} <- verify_style(style)
do
{locale, style, options}
else
{:error, _} = error -> error
end
end
defp verify_unit(locale, style, unit) do
if !pattern_for(locale, style, unit) do
{:error, unit_error(unit)}
else
{:ok, unit}
end
end
defp verify_style(style) do
if !(style in @unit_styles) do
{:error, style_error(style)}
else
{:ok, style}
end
end
end