Packages
ex_cldr
0.0.5
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.
Retired package: Deprecated
Current section
Files
Jump to
Current section
Files
lib/cldr/config.ex
defmodule Cldr.Config do
@moduledoc """
Locales are configured for use in `Cldr` by either
specifying them directly or by using a configured
`Gettext` module.
Locales are configured in `config.exs` (or any included config).
For example the following will configure English and French as
the available locales. Note that only locales that are contained
within the CLDR repository will be available for use. There
are currently 511 locales defined in CLDR.
config :ex_cldr,
locales: ["en", "fr"]
## Working with Gettext
It's also possible to use the locales from a Gettext
configuration:
config :ex_cldr,
locales: ["en", "fr"]
gettext: App.Gettext
In which case the combination of locales "en", "fr" and
whatever is configured for App.Gettext will be generated.
## Locale wildcards
Locales can also be configured by using a `regex` which is most
useful when dealing with locales that have many regional variants
like English (over 100!) and French. For example:
config :ex_cldr,
locales: ["fr-*", "en-[A-Z]+"]
will configure all French locales and all English locales that have
alphabetic regional variants. The expansion is made using
`Regex.match?` so any valid regex can be used.
## Configuring all locales
As a special case, all locales in CLDR can be configured
by using the keyword `:all`. For example:
config :ex_cldr,
locales: :all
**Configuring all locales is not recommended*. Doing so
imposes a significant compilation load as many functions
are created at compmile time for each locale.**
The `Cldr` test configuration does configure all locales in order
to ensure good test coverage. This is done at the expense
of significant compile time.
## Storage location for the locale definiton files
Locale files are downloaded and installed at compile time based upon the
configuration. These files are only used at compile time, they contain
the `json` representation of the locale data.
By default the locale files are stored in `./priv/cldr/locales`.
The locale of the locales can be changed in the configuration with the
`:data_dir` key. For example:
config :ex_cldr,
locales: ["en", "fr"]
data_dir: "/apps/data/cldr"
The directory will be created if it does not exist and an
exception will be raised if the directory cannot be created.
"""
alias Cldr.Locale
import Cldr.Install, only: [install_locale: 1]
@type t :: binary
@default_locale "en"
@cldr_relative_dir "/priv/cldr"
@doc """
Return the root path of the cldr application
"""
@cldr_home_dir Path.join(__DIR__, "/../..") |> Path.expand
def cldr_home do
@cldr_home_dir
end
@doc """
Return the directory where `Cldr` stores its core data
"""
@cldr_data_dir Path.join(@cldr_home_dir, @cldr_relative_dir)
def cldr_data_dir do
@cldr_data_dir
end
# We want to know where the cldr data is stored for an app
# by default. But we need to do some trickery depedning
# on whether ex_cldr is a dependency in a client app or
# whether we're working on cldr itself. The key is the
# assumption that if we're a dependency is a client app then
# "deps" is part of the path
@default_data_dir if(String.contains?(__DIR__, "/deps/"),
do: hd(String.split(__DIR__, "deps")),
else: @cldr_home_dir) <> @cldr_relative_dir
@doc """
Return the path name of the CLDR data directory for a client application.
"""
@data_dir (Application.get_env(:cldr, :data_dir) || @default_data_dir)
|> Path.expand
def data_dir do
@data_dir
end
@doc """
Return the configured `Gettext` module name or `nil`.
"""
@spec gettext :: atom
def gettext do
Application.get_env(:ex_cldr, :gettext)
end
@doc """
Return the default locale.
In order of priority return either:
* The default locale specified in the `mix.exs` file
* The `Gettext.get_locale/1` for the current configuratioh
* "en"
"""
@spec default_locale :: Locale.t
def default_locale do
app_default = Application.get_env(:ex_cldr, :default_locale)
cond do
app_default ->
app_default
gettext_configured?() ->
Gettext
|> apply(:get_locale, [gettext()])
|> Enum.map(&String.replace(&1,"_","-"))
true ->
@default_locale
end
end
@doc """
Return a list of the lcoales defined in `Gettext`.
Return a list of locales configured in `Gettext` or
`[]` if `Gettext` is not configured.
"""
@spec gettext_locales :: [Locale.t]
def gettext_locales do
if gettext_configured?() do
Gettext
|> apply(:known_locales, [gettext()])
|> Enum.map(&String.replace(&1,"_","-"))
else
[]
end
end
@doc """
Returns a list of all locales in the CLDR repository.
Returns a list of the complete locales list in CLDR, irrespective
of whether they are configured for use in the application.
Any configured locales that are not present in this list will
raise an exception at compile time.
"""
@locales_path Path.join(@cldr_data_dir, "available_locales.json")
@all_locales @locales_path
|> File.read!
|> Poison.decode!
|> Enum.sort
@spec all_locales :: [Locale.t]
def all_locales do
@all_locales
end
@doc """
Returns a list of all locales configured in the `config.exs`
file.
In order of priority return either:
* The list of locales configured configured in mix.exs if any
* The default locale
If the configured locales is `:all` then all locales
in CLDR are configured.
This is not recommended since all 511 locales take
quite some time (minutes) to compile. It is however
helpful for testing Cldr.
"""
@spec configured_locales :: [Locale.t]
def configured_locales do
case app_locales = Application.get_env(:ex_cldr, :locales) do
:all -> @all_locales
nil -> [default_locale()]
_ -> expand_locales(app_locales)
end |> Enum.sort
end
@doc """
Returns a list of all locales that are configured and available
in the CLDR repository.
"""
@spec known_locales :: [Locale.t]
def known_locales do
requested_locales()
|> MapSet.new()
|> MapSet.intersection(MapSet.new(all_locales()))
|> MapSet.to_list
|> Enum.sort
end
@doc """
Returns a list of all locales that are configured but not available
in the CLDR repository.
"""
@spec unknown_locales :: [Locale.t]
def unknown_locales do
requested_locales()
|> MapSet.new()
|> MapSet.difference(MapSet.new(all_locales()))
|> MapSet.to_list
|> Enum.sort
end
@doc """
Returns a list of all configured locales.
The list contains locales configured both in `Gettext` and
specified in the mix.exs configuration file as well as the
default locale.
"""
@lint false
@spec requested_locales :: [Locale.t]
def requested_locales do
(configured_locales() ++ gettext_locales() ++ [default_locale()])
|> Enum.uniq
|> Enum.sort
end
@doc """
Returns true if a `Gettext` module is configured in Cldr and
the `Gettext` module is available.
## Example
iex> Cldr.Config.gettext_configured?
false
"""
@spec gettext_configured? :: boolean
def gettext_configured? do
gettext() && Code.ensure_loaded?(Gettext) && Code.ensure_loaded?(gettext())
end
@doc """
Expands wildcards in locale names.
Locales often have region variants (for example en-AU is one of 104
variants in CLDR). To make it easier to configure a language and all
its variants, a locale can be specified as a regex which will
then do a match against all CLDR locales.
## Examples
iex> Cldr.Config.expand_locales(["en-A+"])
["en-AG", "en-AI", "en-AS", "en-AT", "en-AU"]
iex(15)> Cldr.Config.expand_locales(["fr-*"])
["fr", "fr-BE", "fr-BF", "fr-BI", "fr-BJ", "fr-BL", "fr-CA", "fr-CD", "fr-CF",
"fr-CG", "fr-CH", "fr-CI", "fr-CM", "fr-DJ", "fr-DZ", "fr-GA", "fr-GF",
"fr-GN", "fr-GP", "fr-GQ", "fr-HT", "fr-KM", "fr-LU", "fr-MA", "fr-MC",
"fr-MF", "fr-MG", "fr-ML", "fr-MQ", "fr-MR", "fr-MU", "fr-NC", "fr-NE",
"fr-PF", "fr-PM", "fr-RE", "fr-RW", "fr-SC", "fr-SN", "fr-SY", "fr-TD",
"fr-TG", "fr-TN", "fr-VU", "fr-WF", "fr-YT"]
"""
@wildcard_matchers ["*", "+", ".", "["]
@spec expand_locales([Locale.t]) :: [Locale.t]
def expand_locales(locales) do
locale_list = Enum.map(locales, fn locale ->
if String.contains?(locale, @wildcard_matchers) do
Enum.filter(@all_locales, &Regex.match?(Regex.compile!(locale), &1))
else
locale
end
end)
locale_list |> List.flatten |> Enum.uniq
end
@doc """
Returns the location of the json data for a locale or `nil`
if the locale can't be found.
* `locale` is any locale returned from Cldr.known_locales()`
"""
def locale_path(locale) do
relative_locale_path = ["locales/", "#{locale}.json"]
client_path = Path.join(data_dir(), relative_locale_path)
cldr_path = Path.join(cldr_data_dir(), relative_locale_path)
cond do
File.exists?(client_path) -> client_path
File.exists?(cldr_path) -> cldr_path
true -> nil
end
end
@doc """
Read the locale json, decode it and make any necessary transformations.
This is the only place that we read the locale and we only
read it once. All other uses of locale data are references
to this data.
Additionally the intention is that this is read only at compile time
and used to construct accessor functions in other modules so that
during production run there is no file access or decoding.
If a locale file is not found then it is installed.
"""
def get_locale(locale) do
path = locale_path(locale)
{:ok, path} = if path, do: {:ok, path}, else: install_locale(locale)
path
|> File.read!
|> Poison.decode!
|> assert_valid_keys!(locale)
|> Cldr.Map.atomize_keys
|> atomize_number_systems
|> structure_currencies
|> structure_symbols
|> structure_number_formats
|> structure_rbnf
end
# Simple check that the locale content contains what we expect
# by checking it has the keys we used when the locale was consolidated.
defp assert_valid_keys!(content, locale) do
for module <- Cldr.Consolidate.required_modules do
if !Map.has_key?(content, module) and !System.get_env("DEV") do
raise RuntimeError, message:
"Locale file #{inspect locale} is invalid - map key #{inspect module} was not found."
end
end
content
end
# Number systems are stored as atoms, no new
# number systems are ever added at runtime so
# risk to overflowing the atom table is very low.
defp atomize_number_systems(content) do
number_systems = content
|> Map.get(:number_systems)
|> Enum.map(fn {k, v} -> {k, atomize(v)} end)
|> Enum.into(%{})
Map.put(content, :number_systems, number_systems)
end
# Put the currency data into a %Currency{} struct
# and ensure the currency symbol is upcased
defp structure_currencies(content) do
alias Cldr.Currency
currencies = content.currencies
|> Enum.map(fn {code, currency} -> {code, struct(Currency, currency)} end)
|> Enum.into(%{})
Map.put(content, :currencies, currencies)
end
# Put the number_formats into a %Format{} struct
defp structure_number_formats(content) do
alias Cldr.Number.Format
formats = content.number_formats
|> Enum.map(fn {system, format} -> {system, struct(Format, format)} end)
|> Enum.into(%{})
Map.put(content, :number_formats, formats)
end
# Put the symbols into a %Symbol{} struct
defp structure_symbols(content) do
alias Cldr.Number.Symbol
symbols = content.number_symbols
|> Enum.map(fn
{system, nil} -> {system, nil}
{system, symbol} -> {system, struct(Symbol, symbol)}
end)
|> Enum.into(%{})
Map.put(content, :number_symbols, symbols)
end
# Put the rbnf rules into a %Rule{} struct
defp structure_rbnf(content) do
rbnf = content.rbnf
|> Enum.map(fn {group, sets} -> {group, structure_sets(sets)} end)
|> Enum.into(%{})
Map.put(content, :rbnf, rbnf)
end
defp structure_sets(sets) do
alias Cldr.Rbnf.Rule
Enum.map(sets, fn {name, set} ->
rules = Enum.map(set.rules, fn (rule) -> struct(Rule, rule) end)
{name, %{set | rules: rules}}
end)
|> Enum.into(%{})
end
# Convert to an atom but only if
# its a binary.
defp atomize(nil), do: nil
defp atomize(v) when is_binary(v), do: String.to_atom(v)
defp atomize(v), do: v
end