Packages

ex_cldr

1.4.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.

Current section

Files

Jump to
ex_cldr lib mix tasks compile.cldr.ex
Raw

lib/mix/tasks/compile.cldr.ex

defmodule Mix.Tasks.Compile.Cldr do
@moduledoc """
Recompiles Cldr modules when the Cldr locale
configuration changes.
Cldr defines a lot of compile-time functions based
upon the configuration in mix.exs. However since
Cldr is a dependency it is not automatically recompile
when the host application configuration changes.
An additional issue is that dependencies are compiled
before the host application (as they should be) but
that means that a host application `Gettext` module
won't be available and therefore its locales won't be
configured in Cldr.
This module implements a `Mix compiler` that is designed
to execute after the host application is compiled. It then
detects if the Cldr configuration has changed and if so it:
* Searches all dependencies to find those modules that
depend on `Cldr.Config` since that is the module that
serves Cldr configuration content.
* Recompiles each of the dependent modules for each of
app dependencies in the right dependency order
* Reloads the modules that were recompiled
Since this compilation happens after the host application
is compiled, any `Gettext` module will have been compiled
and its configuraiton is now available to Cldr.
## Configuration
To enable the automatic detection locale configuration
change and subsequent recompilation and reload, add the
`[:cldr]` compiler to the `:compilers` section of the
`mix.exs` project definition:
For example:
```
def project do
[
app: :app_name,
...
compilers: Mix.compilers ++ [:cldr]
]
end
```
"""
use Mix.Task
require Logger
@manifest ".compile.cldr"
@callee_references [Cldr.Config]
@dont_recompile @callee_references ++ [Cldr.Locale.Cache, __MODULE__]
def run(_args) do
if configured_locales() != previous_locales() do
IO.puts(
"Locale configuration has changed to [" <>
"#{format_changes(previous_locales(), configured_locales())}]"
)
case compile_cldr_files() do
:ok ->
create_manifest(configured_locales())
_ ->
:noop
end
else
:noop
end
end
def manifests, do: [manifest()]
def manifest do
Path.join(Mix.Project.manifest_path(), @manifest)
end
def compile_cldr_files do
deps = Mix.Dep.loaded(env: Mix.env())
apps_to_compile =
deps
|> modules_to_compile()
|> Enum.group_by(fn {app, _module} -> app end, fn {_app, module} -> module end)
|> print_console_note
compile_results =
deps_in_order(deps, apps_to_compile, %{}, fn {app, modules} ->
build_dir = build_dir(app)
sources = sources(modules)
purge_modules(modules, build_dir)
compile_files(sources, build_dir)
end)
return(compile_results)
end
def return(list) do
return_codes =
Enum.map(list, fn
# Elixir 1.6+ compiler return
{_, {:ok, _, _}} ->
:ok
# Elixir 1.5 compiler return
{_, mods} when is_list(mods) ->
:ok
_ ->
:error
end)
if Enum.all?(return_codes, &(&1 == :ok)), do: :ok, else: :error
end
def print_console_note(apps_to_compile) do
module_count =
Enum.reduce(apps_to_compile, 0, fn {_app, modules}, acc -> acc + Enum.count(modules) end)
if module_count > 0 do
IO.puts(
"Recompiling #{module_count} modules from " <> "#{inspect(Map.keys(apps_to_compile))} "
)
end
apps_to_compile
end
def deps_in_order([], _apps, acc, _fun) do
acc
end
def deps_in_order(deps, apps, already_done, fun) do
Enum.reduce(deps, already_done, fn dep, acc ->
acc = deps_in_order(dep.deps, apps, acc, fun)
app = dep.app
if app in Map.keys(apps) && app not in Map.keys(acc) do
Map.put(acc, app, fun.({app, Map.get(apps, app)}))
else
acc
end
end)
end
def modules_to_compile(nil) do
[]
end
def modules_to_compile(deps) do
for dep <- deps do
callers(dep) ++ modules_to_compile(dep.deps)
end
|> List.flatten()
|> Enum.map(fn
{app, %{caller_module: caller_module}} -> {app, caller_module}
{app, caller} -> {app, caller}
end)
|> Enum.reject(fn
{_app, module} when module in @dont_recompile -> true
_ -> false
end)
|> Enum.uniq()
end
defp callers(dep) do
try do
Mix.Dep.in_dependency(dep, fn _module ->
Mix.Project.build_structure()
calls()
|> Enum.filter(&compile_module?/1)
|> Enum.map(fn callee -> {dep.app, callee} end)
end)
rescue
File.Error ->
[]
end
end
if Version.compare(System.version(), "1.6.0") in [:gt, :eq] do
def compile_files(sources, build_dir) do
Kernel.ParallelCompiler.compile_to_path(sources, build_dir)
end
def calls do
Mix.Tasks.Xref.calls()
end
else
def compile_files(sources, build_dir) do
Kernel.ParallelCompiler.files_to_path(sources, build_dir)
end
def calls do
pre6_calls()
end
end
def compile_module?(%{callee: {reference, _, _}}) do
reference in @callee_references
end
def purge_modules([], _build_dir) do
:ok
end
def purge_modules([module | modules], build_dir) do
:code.purge(module)
:code.delete(module)
beam_file = build_dir <> "/Elixir." <> inspect(module) <> ".beam"
case File.rm(beam_file) do
:ok -> :ok
{:error, :enoent} -> :ok
error -> raise "Cldr compiler could not delete #{beam_file}: #{inspect error}"
end
purge_modules(modules, build_dir)
end
def build_dir(dep) do
Mix.Dep.loaded_by_name([dep], [])
|> hd
|> Map.get(:opts)
|> Keyword.get(:build)
|> String.replace_suffix("", "/ebin")
end
def sources([]) do
[]
end
def sources([module | modules]) do
if source = source(module) do
[source | sources(modules)]
else
sources(modules)
end
end
def source(module) do
case Code.ensure_loaded(module) do
{:module, _} -> List.to_string(module.module_info(:compile)[:source])
_ -> nil
end
end
def create_manifest(locales \\ []) do
File.write!(manifest(), :erlang.term_to_binary(locales))
end
def configured_locales do
Cldr.Config.known_locale_names()
end
def previous_locales do
case File.read(manifest()) do
{:error, :enoent} ->
create_manifest([])
previous_locales()
{:ok, ""} ->
[]
{:ok, binary} ->
:erlang.binary_to_term(binary)
end
end
# Here is where we simulate the results
# of Mix.Tasks.Xref.calls/0 which does
# not exist before Elixir 1.6
@elixir_manifest ".compile.elixir"
@references MapSet.new(@callee_references)
@doc false
def pre6_calls do
manifest_path =
Mix.Project.manifest_path()
|> Path.join(@elixir_manifest)
data =
manifest_path
|> File.read!()
|> :erlang.binary_to_term()
|> Enum.reduce([], fn
version, acc when is_atom(version) ->
acc
{:source, path, _, depends_on, _, _, _, _, _}, acc ->
if cldr_file?(depends_on, @references) do
[path | acc]
else
acc
end
{:module, module, :module, [path], _destination, _}, acc ->
[{path, module} | acc]
_other, acc ->
acc
end)
# Now we have a list where the first part is the
# paths we need and the second part is the module
# defined in the file we want
select_paths = Enum.filter(data, fn p -> if is_binary(p), do: true, else: false end)
potential_modules = Enum.filter(data, fn t -> if is_tuple(t), do: true, else: false end)
Enum.reduce(potential_modules, [], fn {path, module}, acc ->
if path in select_paths do
[
%{
callee: {hd(@callee_references), :no_function, 0},
caller_module: module,
line: 0,
file: ""
}
| acc
]
else
acc
end
end)
end
@doc false
def cldr_file?(calls, references) do
MapSet.size(MapSet.intersection(references, MapSet.new(calls))) > 0
end
@doc false
def format_changes(original, current) do
original
|> List.myers_difference(current)
|> colorize_diff
|> Enum.join(", ")
end
defp colorize_diff([]) do
[]
end
defp colorize_diff([element | tail]) do
case element do
{:eq, items} -> [wrap(items) | colorize_diff(tail)]
{:del, items} -> [wrap(items, IO.ANSI.red()) | colorize_diff(tail)]
{:ins, items} -> [wrap(items, IO.ANSI.green()) | colorize_diff(tail)]
end
end
defp wrap(items, colour \\ "") do
items
|> Enum.map(fn item -> colour <> inspect(item) <> IO.ANSI.reset() end)
|> Enum.join(", ")
end
end