Current section

Files

Jump to
ex_cldr_units lib cldr unit.ex
Raw

lib/cldr/unit.ex

defmodule Cldr.Unit do
@moduledoc """
Supports the CLDR Units definitions which provide for the localization of many
unit types.
The primary public API defines:
* `Cldr.Unit.to_string/3` which, given a number and a unit name or unit list will output a localized string
* `Cldr.Unit.units/0` identifies the available units for localization
* `Cldr.Unit.{add, sub, mult, div}/2` to support basic unit mathematics between
units of compatible type (like length or volume)
* `Cldr.Unit.convert/2` to convert one unit to another unit as long as they
are convertable.
* `Cldr.Unit.decompose/2` to take a unit and return a list of units decomposed
by a list of smaller units.
"""
alias Cldr.Substitution
alias Cldr.LanguageTag
alias Cldr.Locale
alias Cldr.Unit.Conversion
alias Cldr.Unit.Alias
alias Cldr.Unit
alias Cldr.Unit.Math
@enforce_keys [:unit, :value]
defstruct unit: nil, value: 0
@type unit :: atom()
@type style :: atom()
@type t(unit, value_type) :: %Unit{unit: unit, value: value_type}
@type t(unit) :: t(unit, integer | float | Decimal.t)
@type t :: t(atom())
@default_style :long
@styles [:long, :short, :narrow]
defdelegate convert(unit_1, to_unit), to: Cldr.Unit.Conversion
defdelegate add(unit_1, unit_2), to: Cldr.Unit.Math
defdelegate sub(unit_1, unit_2), to: Cldr.Unit.Math
defdelegate mult(unit_1, unit_2), to: Cldr.Unit.Math
defdelegate div(unit_1, unit_2), to: Cldr.Unit.Math
defdelegate add!(unit_1, unit_2), to: Cldr.Unit.Math
defdelegate sub!(unit_1, unit_2), to: Cldr.Unit.Math
defdelegate mult!(unit_1, unit_2), to: Cldr.Unit.Math
defdelegate div!(unit_1, unit_2), to: Cldr.Unit.Math
defdelegate round(unit, places, mode), to: Cldr.Unit.Math
defdelegate round(unit, places), to: Cldr.Unit.Math
defdelegate round(unit), to: Cldr.Unit.Math
@doc """
Returns a new `Unit.t` struct.
## Options
* `value` is any float, integer or `Decimal`
* `unit` is any unit returned by `Cldr.Unit.units`
## Returns
* `unit` or
* `{:error, {exception, message}}`
## Examples
iex> Cldr.Unit.new(23, :gallon)
#Unit<:gallon, 23>
iex> Cldr.Unit.new(:gallon, 23)
#Unit<:gallon, 23>
iex> Cldr.Unit.new(14, :gadzoots)
{:error, {Cldr.UnknownUnitError,
"The unit :gadzoots is not known."}}
"""
def new(value, unit) when is_number(value) do
with {:ok, unit} <- validate_unit(unit) do
%Unit{unit: unit, value: value}
end
end
def new(unit, value) when is_number(value) do
new(value, unit)
end
def new(%Decimal{} = value, unit) do
with {:ok, unit} <- validate_unit(unit) do
%Unit{unit: unit, value: value}
end
end
def new(unit, %Decimal{} = value) do
new(value, unit)
end
@doc """
Returns a new `Unit.t` struct or raises on error.
## Options
* `value` is any float, integer or `Decimal`
* `unit` is any unit returned by `Cldr.Unit.units/0`
## Returns
* `unit` or
* raises an exception
## Examples
iex> Cldr.Unit.new! 23, :gallon
#Unit<:gallon, 23>
Cldr.Unit.new! 14, :gadzoots
** (Cldr.UnknownUnitError) The unit :gadzoots is not known.
(ex_cldr_units) lib/cldr/unit.ex:57: Cldr.Unit.new!/2
"""
def new!(unit, value) do
case new(unit, value) do
{:error, {exception, message}} -> raise exception, message
unit -> unit
end
end
@doc """
Returns a boolean indicating if two units are
of the same unit type.
## Options
* `unit_1` and `unit_2` are any units returned by
`Cldr.Unit.new/2` or units returned by `Cldr.Unit.units/0`
## Returns
* `true` or `false`
## Examples
iex> Cldr.Unit.compatible? :foot, :meter
true
iex> Cldr.Unit.compatible? Cldr.Unit.new!(:foot, 23), :meter
true
iex> Cldr.Unit.compatible? :foot, :liter
false
"""
def compatible?(unit_1, unit_2) do
with {:ok, unit_1} <- validate_unit(unit_1),
{:ok, unit_2} <- validate_unit(unit_2) do
unit_type(unit_1) == unit_type(unit_2) && Conversion.factor(unit_1) != :not_convertible &&
Conversion.factor(unit_2) != :not_convertible
else
_ -> false
end
end
@doc """
Formats a number into a string according to a unit definition for a locale.
## Arguments
* `list_or_number` is any number (integer, float or Decimal) or a
`Cldr.Unit.t()` struct or a list of `Cldr.Unit.t()` structs
* `options` is a keyword list of options
## Options
* `:unit` is any unit returned by `Cldr.Unit.units/2`. Ignored if
the number to be formatted is a `Cldr.Unit.t()` struct
* `:locale` is any valid locale name returned by `Cldr.known_locale_names/1`
or a `Cldr.LanguageTag` struct. The default is `Cldr.get_current_locale/0`
* `:style` is one of those returned by `Cldr.Unit.styles`.
The current styles are `:long`, `:short` and `:narrow`.
The default is `style: :long`
* `:list_options` is a keyword list of options for formatting a list
which is passed through to `Cldr.List.to_string/3`. This is only
applicable when formatting a list of units.
* Any other options are passed to `Cldr.Number.to_string/2`
which is used to format the `number`
## Returns
* `{:ok, formatted_string}` or
* `{:error, {exception, message}}`
## Examples
iex> Cldr.Unit.to_string 123, TestBackend.Cldr, unit: :gallon
{:ok, "123 gallons"}
iex> Cldr.Unit.to_string 1, TestBackend.Cldr, unit: :gallon
{:ok, "1 gallon"}
iex> Cldr.Unit.to_string 1, TestBackend.Cldr, unit: :gallon, locale: "af"
{:ok, "1 gelling"}
iex> Cldr.Unit.to_string 1, TestBackend.Cldr, unit: :gallon, locale: "bs"
{:ok, "1 galon"}
iex> Cldr.Unit.to_string 1234, TestBackend.Cldr, unit: :gallon, format: :long
{:ok, "1 thousand gallons"}
iex> Cldr.Unit.to_string 1234, TestBackend.Cldr, unit: :gallon, format: :short
{:ok, "1K gallons"}
iex> Cldr.Unit.to_string 1234, TestBackend.Cldr, unit: :megahertz
{:ok, "1,234 megahertz"}
iex> Cldr.Unit.to_string 1234, TestBackend.Cldr, unit: :megahertz, style: :narrow
{:ok, "1,234MHz"}
iex> unit = Cldr.Unit.new(123, :foot)
iex> Cldr.Unit.to_string unit, TestBackend.Cldr
{:ok, "123 feet"}
iex> Cldr.Unit.to_string 123, TestBackend.Cldr, unit: :megabyte, locale: "en", style: :unknown
{:error, {Cldr.UnknownFormatError, "The unit style :unknown is not known."}}
iex> Cldr.Unit.to_string 123, TestBackend.Cldr, unit: :blabber, locale: "en"
{:error, {Cldr.UnknownUnitError, "The unit :blabber is not known."}}
"""
@spec to_string(list_or_number :: Cldr.Math.number_or_decimal() | t() | [t(), ...], backend :: Cldr.backend(), options :: Keyword.t()) ::
{:ok, String.t()} | {:error, {atom, binary}}
def to_string(list_or_number, backend, options \\ [])
def to_string(unit_list, backend, options) when is_list(unit_list) do
with {locale, _style, options} <- normalize_options(backend, options),
{:ok, locale} <- backend.validate_locale(locale) do
list_options =
options
|> Keyword.get(:list_options, [])
|> Keyword.put(:locale, locale)
unit_list
|> Enum.map(&to_string!(&1, backend, options ++ [locale: locale]))
|> Cldr.List.to_string!(backend, list_options)
end
end
def to_string(%Unit{unit: unit, value: value}, backend, options) when is_list(options) do
options = Keyword.put(options, :unit, unit)
to_string(value, backend, options)
end
def to_string(number, backend, options) when is_list(options) do
with {locale, style, options} <- normalize_options(backend, options),
{:ok, locale} <- backend.validate_locale(locale),
{:ok, style} <- validate_style(style),
{:ok, unit} <- validate_unit(options[:unit]) do
{:ok, to_string(number, unit, locale, style, backend, options)}
end
end
@doc """
Formats a list using `to_string/3` but raises if there is
an error.
## Arguments
* `number` is any number (integer, float or Decimal) or a
`Cldr.Unit.t()` struct
* `options` is a keyword list
## Options
* `:unit` is any unit returned by `Cldr.Unit.units/2`. Ignored if
the number to be formatted is a `Cldr.Unit.t()` struct
* `:locale` is any valid locale name returned by `Cldr.known_locale_names/0`
or a `Cldr.LanguageTag` struct. The default is `Cldr.get_current_locale/0`
* `: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`
## Returns
* `formatted_string` or
* raises and exception
## Examples
iex> Cldr.Unit.to_string! 123, TestBackend.Cldr, unit: :gallon
"123 gallons"
iex> Cldr.Unit.to_string! 1, TestBackend.Cldr, unit: :gallon
"1 gallon"
iex> Cldr.Unit.to_string! 1, TestBackend.Cldr, unit: :gallon, locale: "af"
"1 gelling"
"""
@spec to_string!(Math.decimal_or_number(), Cldr.backend(), Keyword.t()) :: String.t() | no_return()
def to_string!(number, backend, options \\ []) do
case to_string(number, backend, options) do
{:ok, string} -> string
{:error, {exception, message}} -> raise exception, message
end
end
@spec to_string(
Math.decimal_or_number(),
unit,
Locale.locale_name() | LanguageTag.t(),
style,
Cldr.backend(),
Keyword.t()
) :: String.t()
defp to_string(number, unit, locale, style, backend, options) do
with {:ok, number_string} <- Cldr.Number.to_string(number, backend, options ++ [locale: locale]),
{:ok, patterns} <- pattern_for(locale, style, unit, backend) do
cardinal_module = Module.concat(backend, Number.Cardinal)
pattern = cardinal_module.pluralize(number, locale, patterns)
number_string
|> Substitution.substitute(pattern)
|> :erlang.iolist_to_binary()
end
end
@doc """
Return the value of the Unit struct
## Arguments
* `unit` is any unit returned by `Cldr.Unit.new/2`
## Returns
* an integer, float or Decimal representing the amount
of the unit
## Example
iex> Cldr.Unit.value Cldr.Unit.new(:kilogram, 23)
23
"""
@spec value(unit :: Unit.t()) :: Cldr.Math.number_or_decimal()
def value(%Unit{value: value}) do
value
end
@data_dir [:code.priv_dir(:ex_cldr), "/cldr/locales"] |> :erlang.iolist_to_binary
@config %{data_dir: @data_dir, locales: ["en"], default_locale: "en"}
@unit_tree "en"
|> Cldr.Config.get_locale(@config)
|> Map.get(:units)
|> Map.get(:short)
|> Enum.map(fn {k, v} -> {k, Map.keys(v)} end)
|> Enum.into(%{})
@doc """
Decomposes a unit into subunits.
Any list compatible units can be provided
however a list of units of decreasing scale
is recommended. For example `[:foot, :inch]`
or `[:kilometer, :meter, :centimeter, :millimeter]`
## Examples
iex> u = Cldr.Unit.new(10.3, :foot)
iex> Cldr.Unit.decompose u, [:foot, :inch]
[Cldr.Unit.new(:foot, 10), Cldr.Unit.new(:inch, 4)]
iex> u = Cldr.Unit.new(:centimeter, 1111)
iex> Cldr.Unit.decompose u, [:kilometer, :meter, :centimeter, :millimeter]
[Cldr.Unit.new(:meter, 11), Cldr.Unit.new(:centimeter, 11)]
"""
@spec decompose(Unit.t(), [Unit.unit(), ...]) :: [Unit.t(), ...] | {:error, {module(), String.t()}}
def decompose(unit, []) do
[unit]
end
def decompose(unit, [h | []]) do
new_unit =
unit
|> Conversion.convert!(h)
|> Math.round
|> Math.trunc
if zero?(new_unit) do
[]
else
[new_unit]
end
end
def decompose(unit, [h | t]) do
new_unit = Conversion.convert!(unit, h)
{integer_unit, remainder} = int_rem(new_unit)
if zero?(integer_unit) do
decompose(remainder, t)
else
[integer_unit | decompose(remainder, t)]
end
end
defp int_rem(unit) do
integer = Unit.round(unit, 0, :down) |> Math.trunc
remainder = Math.sub(unit, integer)
{integer, remainder}
end
@doc """
Returns a new unit of the same unit
type but with a zero value.
## Example
iex> u = Cldr.Unit.new(:foot, 23.3)
#Unit<:foot, 23.3>
iex> Cldr.Unit.zero(u)
#Unit<:foot, 0.0>
"""
def zero(%Unit{value: value} = unit) when is_integer(value) do
%Unit{unit | value: 0}
end
def zero(%Unit{value: value} = unit) when is_float(value) do
%Unit{unit | value: 0.0}
end
def zero(%Unit{} = unit) do
%Unit{unit | value: Decimal.new(0)}
end
@doc """
Returns a boolean indicating whether a given unit
has a zero value.
## Examples
iex> u = Cldr.Unit.new(:foot, 23.3)
#Unit<:foot, 23.3>
iex> Cldr.Unit.zero?(u)
false
iex> u = Cldr.Unit.new(:foot, 0)
#Unit<:foot, 0>
iex> Cldr.Unit.zero?(u)
true
"""
def zero?(%Unit{value: value}) when is_number(value) do
value == 0
end
@decimal_0 Decimal.new(0)
def zero?(%Unit{value: value}) do
Decimal.cmp(value, @decimal_0) == :eq
end
@doc """
Returns a list of the known unit categories.
## Example
iex> Cldr.Unit.unit_types
[:acceleration, :angle, :area, :concentr, :consumption, :coordinate, :digital,
:duration, :electric, :energy, :frequency, :length, :light, :mass, :power,
:pressure, :speed, :temperature, :volume]
"""
@unit_types Map.keys(@unit_tree)
def unit_types do
@unit_types
end
@doc """
Returns a list of the unit types and associated
units
## Example
Cldr.Unit.unit_tree
=> %{
acceleration: [:g_force, :meter_per_second_squared],
angle: [:arc_minute, :arc_second, :degree, :radian, :revolution],
area: [:acre, :hectare, :square_centimeter, :square_foot, :square_inch,
:square_kilometer, :square_meter, :square_mile, :square_yard],
concentr: [:karat, :milligram_per_deciliter, :millimole_per_liter,
:part_per_million]
...
"""
@spec unit_tree :: [atom, ...]
def unit_tree do
@unit_tree
end
@doc """
Returns the units associated with a given unit type
## Options
* `unit` is any units returned by
`Cldr.Unit.new/2` or units returned by `Cldr.Unit.units/0`
## Returns
* a valid unit or
* `{:error, {exception, message}}`
## Examples
iex> Cldr.Unit.unit_type :pint_metric
:volume
iex> Cldr.Unit.unit_type :stone
:mass
"""
@spec unit_type(Unit.t() | String.t() | atom()) ::
atom() | {:error, {Exception.t(), String.t()}}
def unit_type(unit) do
with {:ok, unit} <- validate_unit(unit) do
type_map(unit)
end
end
@doc """
Returns the known units.
## Example
Cldr.Unit.units
[:acre, :acre_foot, :ampere, :arc_minute, :arc_second, :astronomical_unit, :bit,
:bushel, :byte, :calorie, :carat, :celsius, :centiliter, :centimeter, :century,
:cubic_centimeter, :cubic_foot, :cubic_inch, :cubic_kilometer, :cubic_meter,
:cubic_mile, :cubic_yard, :cup, :cup_metric, :day, :deciliter, :decimeter,
:degree, :fahrenheit, :fathom, :fluid_ounce, :foodcalorie, :foot, :furlong,
:g_force, :gallon, :gallon_imperial, :generic, :gigabit, :gigabyte, :gigahertz,
:gigawatt, :gram, :hectare, :hectoliter, :hectopascal, :hertz, :horsepower,
:hour, :inch, ...]
"""
@units @unit_tree
|> Map.values()
|> List.flatten()
@spec units :: [atom, ...]
def units do
@units
end
@doc """
Returns the units for a given unit type
## Arguments
* `type` is any unit type returned by
`Cldr.Unit.unit_types/0`
## Returns
* a list of units
## Examples
iex> Cldr.Unit.units(:length)
[:astronomical_unit, :centimeter, :decimeter, :fathom, :foot, :furlong, :inch,
:kilometer, :light_year, :meter, :micrometer, :mile, :mile_scandinavian,
:millimeter, :nanometer, :nautical_mile, :parsec, :picometer, :point, :yard]
"""
@spec units(atom) :: [atom, ...]
def units(type) when type in @unit_types do
Map.get(unit_tree(), type)
end
def units(type) do
{:error, unit_type_error(type)}
end
@doc """
Returns a list of units that are within the
specified jaro distance of the provided unit.
## Arguments
* `unit` is any unit returned by `Cldr.Unit.units/0` or by
`Cldr.Unit.new/2`
* `distance` is a float between 0.0 and 1.0 representing
the jaro distance above which a unit must match in order
to be returned. The default is 0.75
## Returns
* a list of tagged tuples of the form `{jaro_distance, unit}`
sorted in decending jaro distance order or
* `{:error, {exception, message}}`
## Examples
iex> Cldr.Unit.jaro_match :foot
[{1.0, :foot}]
iex> Cldr.Unit.jaro_match :meter
[
{1.0, :meter},
{0.7708333333333334, :meter_per_second},
{0.7592592592592592, :kilometer_per_hour}
]
"""
@default_distance 0.75
@spec jaro_match(unit, number) :: [{float, unit}, ...] | []
def jaro_match(unit, distance \\ @default_distance)
def jaro_match(%Unit{unit: unit}, distance) do
jaro_match(unit, distance)
end
def jaro_match(unit, distance) do
unit
|> Kernel.to_string()
|> match_list(distance)
end
@doc """
Returns the unit closed in jaro distance to the
provided unit
## Arguments
* `unit` is any unit returned by `Cldr.Unit.units/0` or by
`Cldr.Unit.new/2`
* `distance` is a float between 0.0 and 1.0 representing
the jaro distance above which a unit must match in order
to be returned. The default is 0.75
## Returns
* a `Unit.t` struct or
* `nil`
## Examples
iex> Cldr.Unit.best_match :ft
:fathom
iex> Cldr.Unit.best_match :zippity
nil
"""
def best_match(unit, distance \\ @default_distance) do
unit
|> jaro_match(distance)
|> return_best_match
end
defp return_best_match([]) do
nil
end
defp return_best_match([{_distance, unit} | _rest]) do
unit
end
@doc """
Returns a list of units that are compatible with the
provided unit.
## Arguments
* `unit` is any unit returned by `Cldr.Unit.units/0` or by
`Cldr.Unit.new/2`
* `options` is a keyword list of options
## Options
* `:jaro` is a boolean which determines if the match
is to use the jaro distance. The default is `false`
* `distance` is a float between 0.0 and 1.0 representing
the jaro distance above which a unit must match in order
to be returned. The default is 0.75
## Returns
* a list of tuples of the form `{jaro_distance, unit}`
sorted in decending jaro distance order, or
* `{:error, {exception, message}}`
## Examples
iex> Cldr.Unit.compatible_units :foot
[:astronomical_unit, :centimeter, :decimeter, :fathom, :foot, :furlong, :inch,
:kilometer, :light_year, :meter, :micrometer, :mile, :mile_scandinavian,
:millimeter, :nanometer, :nautical_mile, :parsec, :picometer, :point, :yard]
iex> Cldr.Unit.compatible_units :me, jaro: true
[{0.7999999999999999, :meter}]
"""
@default_options [jaro: false, distance: @default_distance]
@spec compatible_units(unit, Keyword.t() | Map.t()) :: [unit, ...] | [{float, unit}, ...] | []
def compatible_units(unit, options \\ @default_options)
def compatible_units(unit, options) when is_list(options) do
options = Keyword.merge(@default_options, options) |> Enum.into(%{})
compatible_units(unit, options)
end
def compatible_units(unit, %{jaro: false}) do
with {:ok, unit} <- validate_unit(unit) do
type = unit_type(unit)
unit_tree()[type]
end
end
def compatible_units(unit, %{jaro: true, distance: distance}) when is_number(distance) do
unit = Kernel.to_string(unit)
case jaro_match(unit, distance) do
jaro_list when is_list(jaro_list) -> compatible_list(jaro_list, unit)
other -> other
end
end
@string_units Enum.map(@units, &Atom.to_string/1)
@spec match_list(unit, number) :: [{float, unit}, ...] | []
defp match_list(unit, distance) when is_binary(unit) and is_number(distance) do
@string_units
|> Enum.map(fn u -> {String.jaro_distance(unit, u), String.to_existing_atom(u)} end)
|> Enum.filter(&(elem(&1, 0) >= distance))
|> Enum.sort(&(elem(&1, 0) > elem(&2, 0)))
end
@spec compatible_list([{float, unit}, ...], unit) :: [{float, unit}, ...] | []
defp compatible_list(jaro_list, unit) when is_list(jaro_list) do
with {:ok, unit} <- validate_unit(unit) do
Enum.filter(jaro_list, fn
{_distance, u} -> unit_type(u) == unit_type(unit)
u -> unit_type(u) == unit_type(unit)
end)
else
_ ->
# Use the best match as the match key
[{_, unit} | _] = jaro_list
compatible_list(jaro_list, unit)
end
end
@doc """
Returns the known styles for a unit.
## Example
iex> Cldr.Unit.styles
[:long, :short, :narrow]
"""
def styles do
@styles
end
@doc """
Returns the default formatting style.
## Example
iex> Cldr.Unit.default_style
:long
"""
def default_style do
@default_style
end
defp type_map(unit) do
with {:ok, unit} <- validate_unit(unit) do
type_map()
|> Map.get(unit)
end
end
def units_for(locale, style, backend) do
module = Module.concat(backend, :'Elixir.Unit')
module.units_for(locale, style)
end
defp pattern_for(%LanguageTag{cldr_locale_name: locale_name}, style, unit, backend) do
with {:ok, style} <- validate_style(style),
{:ok, unit} <- validate_unit(unit) do
units = units_for(locale_name, style, backend)
pattern = Map.get(units, unit)
{:ok, pattern}
end
end
defp pattern_for(locale_name, style, unit, backend) do
with {:ok, locale} <- backend.validate_locale(locale_name) do
pattern_for(locale, style, unit, backend)
end
end
@type_map @unit_tree
|> Enum.map(fn {k, v} ->
k = List.duplicate(k, length(v))
Enum.zip(v, k)
end)
|> List.flatten()
|> Enum.into(%{})
defp type_map do
@type_map
end
defp normalize_options(backend, options) do
locale = options[:locale] || backend.get_locale()
style = options[:style] || @default_style
options =
options
|> Keyword.delete(:locale)
|> Keyword.delete(:style)
{locale, style, options}
end
@doc """
Validates a unit name and normalizes it to a
standard downcased atom form
"""
def validate_unit(unit) when unit in @units do
{:ok, unit}
end
@aliases Alias.aliases() |> Map.keys()
def validate_unit(unit) when unit in @aliases do
unit
|> Alias.alias()
|> validate_unit
end
def validate_unit(unit) when is_binary(unit) do
unit
|> String.downcase()
|> String.to_existing_atom()
|> validate_unit()
rescue
ArgumentError ->
{:error, unit_error(unit)}
end
def validate_unit(%Unit{unit: unit}) do
{:ok, unit}
end
def validate_unit(unit) do
{:error, unit_error(unit)}
end
@doc """
Validates a unit style and normalizes it to a
standard downcased atom form
"""
def validate_style(style) when style in @styles do
{:ok, style}
end
def validate_style(style) when is_binary(style) do
style
|> String.downcase()
|> String.to_existing_atom()
|> validate_style()
catch
ArgumentError ->
{:error, style_error(style)}
end
def validate_style(style) do
{:error, style_error(style)}
end
@doc false
def unit_error(nil) do
{
Cldr.UnknownUnitError,
"A unit must be provided, for example 'Cldr.Unit.string(123, unit: :meter)'."
}
end
def unit_error(unit) do
{Cldr.UnknownUnitError, "The unit #{inspect(unit)} is not known."}
end
@doc false
def unit_type_error(type) do
{Cldr.Unit.UnknownUnitTypeError, "The unit type #{inspect(type)} is not known."}
end
@doc false
def style_error(style) do
{Cldr.UnknownFormatError, "The unit style #{inspect(style)} is not known."}
end
@doc false
def incompatible_units_error(unit_1, unit_2) do
{
Unit.IncompatibleUnitsError,
"Operations can only be performed between units of the same type. " <>
"Received #{inspect(unit_1)} and #{inspect(unit_2)}"
}
end
end