Packages

ex_cldr

2.24.0-rc.3
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 config rbnf_config.ex
Raw

lib/cldr/config/rbnf_config.ex

defmodule Cldr.Rbnf.Config do
@moduledoc false
alias Cldr.Locale
@default_radix 10
@doc """
Returns the directory where the production
RBNF data is stored.
## Example
iex> Cldr.Rbnf.Config.rbnf_dir =~ "/cldr-rbnf/rbnf"
true
"""
@spec rbnf_dir :: String.t()
def rbnf_dir do
data_dir = Path.join(Cldr.Config.download_data_dir(), "/cldr-rbnf")
Path.join(data_dir, "rbnf")
end
@doc """
Returns a list of the locales for which there is an rbnf rule set
Relies on the presence of downloaded CLDR data. This function is
usefully primarily to a Cldr library developer.
## Example
iex> Cldr.Rbnf.Config.rbnf_locales
["af", "am", "ar", "az", "be", "bg", "bs", "ca", "cs", "cy", "da", "de-CH",
"de", "ee", "el", "en", "eo", "es-419", "es", "et", "fa-AF", "fa", "fi", "fil",
"fo", "fr-BE", "fr-CH", "fr", "ga", "he", "hi", "hr", "hu", "hy", "id", "is",
"it", "ja", "ka", "kl", "km", "ko", "ky", "lo", "lt", "lv", "mk", "ms", "mt",
"my", "nb", "nl", "nn", "pl", "pt-PT", "pt", "ro", "ru", "se", "sk",
"sl", "sq", "sr-Latn", "sr", "sv", "ta", "th", "tr", "uk", "und", "vi", "yue",
"zh-Hant", "zh"]
"""
@spec rbnf_locale_names :: [String.t()] | []
def rbnf_locale_names do
Enum.map(File.ls!(rbnf_dir()), &Path.basename(&1, ".json"))
end
@doc """
Returns the list of locales that is the intersection of
`Cldr.known_locale_names/1` and `Cldr.Rbnf.rbnf_locales/0`
This list is therefore the set of known locales for which
there are rbnf rules defined.
## Example
iex> Cldr.Rbnf.Config.known_locale_names
["lo", "eo", "ja", "el", "fo", "hu", "yue", "fil", "sq", "cy", "da", "sv", "ee",
"et", "ta", "nl", "vi", "nb", "lv", "id", "pt-PT", "fa-AF", "lt", "my",
"sr-Latn", "cs", "ms", "fa", "bg", "es", "en", "af", "mt", "am", "ca", "mk",
"ro", "de-CH", "ka", "hr", "nn", "hy", "pt", "se", "he", "ga", "sr",
"hi", "ky", "ko", "zh-Hant", "kl", "km", "sk", "ru", "zh", "de", "fi", "it",
"be", "pl", "az", "tr", "is", "fr-CH", "es-419", "th", "fr-BE", "fr", "sl",
"bs", "uk", "und", "ar"]
"""
def known_locale_names(backend) do
MapSet.intersection(
MapSet.new(Cldr.known_locale_names(backend)),
MapSet.new(rbnf_locale_names())
)
|> MapSet.to_list()
end
@doc """
Returns the rbnf rules for a `locale` or `{:error, :rbnf_file_not_found}`
* `locale_name` is any locale returned by `Rbnf.known_locales/0`.
Note that `for_locale/1` does not raise if the locale does not exist
like the majority of `Cldr`. This is by design since the set of locales
that have rbnf rules is substantially less than the set of locales
supported by `Cldr`.
## Example
iex> {:ok, rules} = Cldr.Rbnf.Config.for_locale("en")
iex> Map.keys(rules)
[:OrdinalRules, :SpelloutRules]
"""
@spec for_locale(Locale.locale_name()) ::
{:ok, map()} | {:error, {Cldr.Rbnf.NotAvailable, String.t()}}
# TODO Use fallback paths to find RBNF data
# Use Cldr.Locale.first_match/2 with a fun that returns
# a valid RBNF locale
def for_locale(locale_name) when is_binary(locale_name) do
with true <- File.exists?(locale_path(locale_name)) do
rules =
locale_name
|> locale_path
|> File.read!()
|> Cldr.Config.json_library().decode!()
|> Map.get("rbnf")
|> Map.get("rbnf")
|> rules_from_rule_sets
{:ok, rules}
else
{:error, {exception, reason}} ->
{:error, {exception, reason}}
false ->
{:error, rbnf_nofile_error(locale_name)}
end
end
defp rbnf_nofile_error(locale_name) do
{
Cldr.Rbnf.NotAvailable,
"The locale name #{inspect(locale_name)} does not have an RBNF configuration file available"
}
end
defp rules_from_rule_sets(json) do
Enum.map(json, fn {group, sets} ->
{String.to_atom(group), rules_from_one_group(sets)}
end)
|> Enum.into(%{})
end
defp rules_from_one_group(sets) do
Enum.map(sets, fn {set, rules} ->
access = access_from_set(set)
{function_name_from(set), %{access: access, rules: rules_from(rules)}}
end)
|> Enum.into(%{})
end
defp access_from_set(<<"%%", _rest::binary>>), do: :private
defp access_from_set(_), do: :public
defp rules_from(rules) do
Enum.map(rules, fn [name, rule] ->
{base_value, radix} = radix_from_name(name)
%{base_value: base_value, radix: radix, definition: remove_trailing_semicolon(rule)}
end)
|> sort_rules
|> set_range
|> set_divisor
end
defp sort_rules(rules) do
Enum.sort(rules, fn a, b ->
cond do
is_binary(a.base_value) -> true
is_binary(b.base_value) -> false
a.base_value < b.base_value -> true
true -> false
end
end)
end
defp radix_from_name(name) do
case String.split(name, "/") do
[base_value, radix] ->
{to_integer(base_value), to_integer(radix)}
[base_value] ->
{to_integer(base_value), @default_radix}
end
end
@spec locale_path(binary) :: String.t()
defp locale_path(locale) when is_binary(locale) do
Path.join(rbnf_dir(), "/#{locale}.json")
end
defp to_integer(value) do
with {int, ""} <- Integer.parse(value) do
int
else
_ -> value
end
end
defp remove_trailing_semicolon(text) do
String.replace_suffix(text, ";", "")
end
# If the current rule is numeric and the next rule is numeric then
# the next rules value determines the upper bound of the validity
# of the current rule.
#
# ie. "0": "one;"
# "10": "ten;"
#
# Means that rule "0" is applied for values up to but not including "10"
defp set_range([rule | [next_rule | rest]]) do
[Map.put(rule, :range, range_from_next_rule(rule.base_value, next_rule.base_value))] ++
set_range([next_rule] ++ rest)
end
defp set_range([rule | []]) do
[Map.put(rule, :range, :undefined)]
end
defp range_from_next_rule(rule, next_rule) when is_number(rule) and is_number(next_rule) do
next_rule
end
defp range_from_next_rule(_rule, _next_rule) do
:undefined
end
defp set_divisor([rule]) do
[Map.put(rule, :divisor, divisor(rule.base_value, rule.radix))]
end
defp set_divisor([rule | rest]) do
[Map.put(rule, :divisor, divisor(rule.base_value, rule.radix)) | set_divisor(rest)]
end
# Thanks to twitter-cldr:
# https://github.com/twitter/twitter-cldr-rb/blob/master/lib/twitter_cldr/formatters/numbers/rbnf/rule.rb
defp divisor(base_value, radix) when is_integer(base_value) and is_integer(radix) do
exponent =
if base_value > 0 do
Float.ceil(:math.log(base_value) / :math.log(radix)) |> trunc
else
1
end
divisor =
if exponent > 0 do
trunc(:math.pow(radix, exponent))
else
1
end
if divisor > base_value do
trunc(:math.pow(radix, exponent - 1))
else
divisor
end
end
defp divisor(_base_value, _radix) do
nil
end
defp function_name_from(set) do
set
|> String.trim_leading("%")
|> Cldr.String.to_underscore()
|> String.to_atom()
end
end