Packages

ex_cldr

1.3.2
2.47.5 2.47.4 2.47.3 2.47.2 2.47.1 2.47.0 2.46.0 2.45.2 2.45.1 2.45.0 2.44.1 2.44.0 2.43.2 2.43.1 2.43.0 2.42.0 2.41.1 2.41.0 2.40.4 2.40.3 2.40.2 2.40.1 2.40.0 2.39.2 2.39.1 2.39.0 2.38.1 2.38.0 2.37.5 2.37.4 2.37.3 retired 2.37.2 2.37.1 2.37.0 2.36.0 2.35.1 2.35.0 retired 2.34.2 2.34.1 2.34.0 2.33.2 2.33.1 2.33.1-rc.0 2.33.0 2.32.1 2.32.0 2.31.0 2.30.0 2.29.0 2.28.0 2.27.1 2.27.0 retired 2.26.3 2.26.2 2.26.1 2.26.0 2.25.0 2.24.2 2.24.1 2.24.0 2.24.0-rc.6 2.24.0-rc.5 2.24.0-rc.4 2.24.0-rc.3 2.24.0-rc.2 2.24.0-rc.1 2.24.0-rc.0 2.23.2 2.23.1 2.23.0 2.22.1 2.22.0 2.21.0 2.20.0 2.20.0-rc.3 2.20.0-rc.2 2.20.0-rc.1 2.20.0-rc.0 2.19.1 2.19.0 2.18.2 2.18.1 2.18.0 2.18.0-rc.0 2.17.2 2.17.1 2.17.0 2.16.2 2.16.1 2.16.1-rc.0 2.16.0 2.15.0 2.14.1 2.14.0 2.14.0-rc.0 2.13.0 2.12.1 2.12.0 2.11.1 2.11.0 2.10.2 2.10.1 2.10.0 retired 2.9.0 2.8.1 2.8.0 2.7.2 2.7.1 2.7.0 2.6.2 2.6.1 2.6.0 2.5.0 2.4.3 2.4.2 2.4.1 2.4.0 2.3.2 2.3.1 2.3.0 2.2.7 2.2.6 2.2.5 2.2.4 2.2.3 2.2.2 2.2.1 2.2.0 2.1.0 2.0.4 2.0.3 2.0.2 2.0.1 2.0.0 2.0.0-rc.3 retired 2.0.0-rc.2 retired 2.0.0-rc.1 retired 2.0.0-rc.0 retired 1.8.2 1.8.1 1.8.0 1.7.1 1.7.0 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.2 1.5.1 1.5.0 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 retired 1.4.0-rc.3 retired 1.4.0-rc.2 retired 1.4.0-rc.1 retired 1.4.0-rc.0 retired 1.3.2 1.3.1 1.3.0 1.2.0 1.1.0 1.0.0 1.0.0-rc.3 retired 1.0.0-rc.2 retired 1.0.0-rc.1 retired 1.0.0-rc.0 retired 0.13.1 0.13.0 0.12.2 retired 0.12.1 retired 0.12.0 retired 0.11.0 retired 0.10.0 retired 0.9.0 retired 0.8.3 retired 0.8.2 retired 0.8.1 retired 0.8.0 retired 0.7.0 retired 0.6.2 retired 0.6.1 retired 0.6.0 retired 0.5.2 retired 0.5.1 retired 0.5.0 retired 0.4.2 retired 0.4.1 retired 0.4.0 retired 0.3.0 retired 0.2.1 retired 0.2.0 retired 0.1.3 retired 0.1.2 retired 0.1.1 retired 0.1.0 retired 0.0.20 retired 0.0.19 retired 0.0.18 retired 0.0.17 retired 0.0.16 retired 0.0.15 retired 0.0.14 retired 0.0.13 retired 0.0.12 retired 0.0.11 retired 0.0.10 retired 0.0.8 retired 0.0.7 retired 0.0.6 retired 0.0.5 retired 0.0.4 retired 0.0.3 retired

Common Locale Data Repository (CLDR) functions for Elixir to localize and format numbers, dates, lists, messages, languages, territories and units with support for over 700 locales for internationalized (i18n) and localized (L10N) applications.

Current section

Files

Jump to
ex_cldr lib cldr plural_rules plural_rule.ex
Raw

lib/cldr/plural_rules/plural_rule.ex

defmodule Cldr.Number.PluralRule do
@moduledoc """
Macro to define plural rule methods on a module.
Used to generate plural rule functions for
`Cldr.Number.Ordinal` and `Cldr.Number.Cardinal`.
"""
defmacro __using__(opts) do
unless opts in [:cardinal, :ordinal] do
raise ArgumentError,
"Invalid option #{inspect(opts)}. :cardinal or :ordinal are the only valid options"
end
quote location: :keep do
alias Cldr.Math
alias Cldr.LanguageTag
alias Cldr.Locale
import Cldr.Digits,
only: [number_of_integer_digits: 1, remove_trailing_zeros: 1, fraction_as_integer: 2]
import Cldr.Number.PluralRule.Compiler
import Cldr.Number.PluralRule.Transformer
@module_name Atom.to_string(unquote(opts)) |> String.capitalize()
@rules Cldr.Config.cldr_data_dir()
|> Path.join("/plural_rules.json")
|> File.read!()
|> Cldr.Config.json_library().decode!
|> Map.get(Atom.to_string(unquote(opts)))
@rules_locales @rules
|> Map.keys()
|> Enum.sort()
@doc """
The locales for which plural rules are defined
"""
def available_locale_names do
@rules_locales
end
@known_locale_names @rules_locales
|> MapSet.new()
|> MapSet.intersection(MapSet.new(Cldr.known_locale_names()))
|> MapSet.to_list()
|> Enum.sort()
@doc """
The configured locales for which plural rules are defined.
Returns the intersection of `Cldr.known_locale_names/0` and
the locales for which #{@module_name} plural rules are defined.
There are many `Cldr` locales which don't have their own plural
rules so this list is the intersection of `Cldr`'s configured
locales and those that have rules.
"""
@spec known_locale_names :: [Locale.locale_name(), ...]
def known_locale_names do
@known_locale_names
end
@doc """
Returns all the plural rules defined in CLDR.
"""
@spec plural_rules :: Map.t()
def plural_rules do
@rules
end
@doc """
Pluralize a number using plural rules and a substition map.
* `number` is an integer, float or Decimal
* `locale` is any locale returned by `Cldr.Locale.new!/1`
* `substitutions` is a map that maps plural keys to a string.
The valid substitution keys are `:zero`, `:one`, `:two`,
`:few`, `:many` and `:other`.
See also `Cldr.#{@module_name}.plural_rule/3`.
## Examples
iex> Cldr.Number.#{@module_name}.pluralize 1,
...> Locale.new("en"), %{one: "one"}
"one"
iex> Cldr.Number.#{@module_name}.pluralize 2,
...> Locale.new("en"), %{one: "one"}
nil
iex> Cldr.Number.#{@module_name}.pluralize 2,
...> Locale.new("en"), %{one: "one", two: "two"}
"two"
iex> Cldr.Number.#{@module_name}.pluralize 22, Locale.new("en"),
...> %{one: "one", two: "two", other: "other"}
"other"
iex> Cldr.Number.#{@module_name}.pluralize Decimal.new(1),
...> Locale.new("en"), %{one: "one"}
"one"
iex> Cldr.Number.#{@module_name}.pluralize Decimal.new(2),
...> Locale.new("en"), %{one: "one"}
nil
iex> Cldr.Number.#{@module_name}.pluralize Decimal.new(2),
...> Locale.new("en"), %{one: "one", two: "two"}
"two"
"""
@default_substitution :other
@spec pluralize(Math.number_or_decimal(), LanguageTag.t(), %{}) :: any()
def pluralize(number, %LanguageTag{} = locale, %{} = substitutions) when is_number(number) do
do_pluralize(number, locale, substitutions)
end
def pluralize(%Decimal{} = number, %LanguageTag{} = locale, %{} = substitutions) do
do_pluralize(number, locale, substitutions)
end
defp do_pluralize(number, %LanguageTag{} = locale, %{} = substitutions) do
plural = plural_rule(number, locale)
substitutions[plural] || substitutions[@default_substitution]
end
@doc """
Return the plural rules for a locale.
The rules are returned in AST form after parsing. This function
is primarilty to support `Cldr.Gettext`.
"""
@spec plural_rules_for(Locale.locale_name()) :: [{atom(), list()}, ...]
def plural_rules_for(locale_name) do
Enum.map(plural_rules()[locale_name], fn {"pluralRule-count-" <> category, rule} ->
{:ok, definition} = parse(rule)
{String.to_atom(category), definition}
end)
end
# Plural Operand Meanings as defined in CLDR plural rules and used
# in the generated code
#
# Symbol Value
# n absolute value of the source number (integer and decimals).
# i integer digits of n.
# v number of visible fraction digits in n, with trailing zeros.
# w number of visible fraction digits in n, without trailing zeros.
# f visible fractional digits in n, with trailing zeros.
# t visible fractional digits in n, without trailing zeros.
@doc """
Return the plural key for a given number in a given locale
Returns which plural key (`:zero`, `:one`, `:two`, `:few`,
`:many` or `:other`) a given number fits into within the
context of a given locale.
Note that these key names should not be interpreted
literally. For example, the key returned from
`Cldr.Number.Ordinal.plural_rule(0, "en")` is actually
`:other`, not `:zero`.
This key can then be used to format a number, date, time, unit,
list or other content in a plural-sensitive way.
## Examples
iex> Cldr.Number.#{@module_name}.plural_rule 0, "fr"
:other
iex> Cldr.Number.#{@module_name}.plural_rule 1, "en"
:one
"""
@spec plural_rule(Math.number_or_decimal(), Locale.locale_name() | LanguageTag.t(), atom()) ::
:zero | :one | :two | :few | :many | :other
def plural_rule(
number,
locale \\ Cldr.get_current_locale(),
rounding \\ Math.default_rounding()
)
def plural_rule(number, locale_name, rounding) when is_binary(locale_name) do
with {:ok, locale} <- Cldr.Locale.new(locale_name) do
plural_rule(number, locale, rounding)
end
end
def plural_rule(number, locale, rounding) when is_binary(number) do
plural_rule(Decimal.new(number), locale, rounding)
end
# Plural rule for an integer
def plural_rule(number, locale, _rounding) when is_integer(number) do
n = abs(number)
i = n
v = 0
w = 0
f = 0
t = 0
do_plural_rule(locale, n, i, v, w, f, t)
end
# Plural rule for a float
def plural_rule(number, locale, rounding)
when is_float(number) and is_integer(rounding) and rounding > 0 do
# Testing shows that this is working but just in case we
# can go back to casting the number to a decimal and
# using that path
# plural_rule(Decimal.new(number), locale, rounding)
n = Float.round(abs(number), rounding)
i = trunc(n)
v = rounding
t = fraction_as_integer(n - i, rounding)
w = number_of_integer_digits(t)
f = trunc(t * Math.power_of_10(v - w))
do_plural_rule(locale, n, i, v, w, f, t)
end
# Plural rule for a %Decimal{}
def plural_rule(%Decimal{} = number, locale, rounding)
when is_integer(rounding) and rounding > 0 do
# n absolute value of the source number (integer and decimals).
n = Decimal.abs(number)
# i integer digits of n.
i = Decimal.round(n, 0, :floor)
# v number of visible fraction digits in n, with trailing zeros.
v = abs(n.exp)
# f visible fractional digits in n, with trailing zeros.
f =
n
|> Decimal.sub(i)
|> Decimal.mult(Decimal.new(Math.power_of_10(v)))
|> Decimal.round(0, :floor)
|> Decimal.to_integer()
# t visible fractional digits in n, without trailing zeros.
t = remove_trailing_zeros(f)
# w number of visible fraction digits in n, without trailing zeros.
w = number_of_integer_digits(t)
i = Decimal.to_integer(i)
n = Math.to_float(n)
do_plural_rule(locale, n, i, v, w, f, t)
end
end
end
end