Packages
ex_cldr_lists
2.12.2
2.12.2
2.12.1
2.12.0
2.11.1
2.11.0
2.10.2
2.10.1
2.10.0
2.9.0
2.9.0-rc.2
2.9.0-rc.1
2.9.0-rc.0
2.8.0
2.8.0-rc.0
2.7.0
2.7.0-rc.0
2.6.1
2.6.0
2.5.0
2.5.0-rc.0
2.4.0
2.3.0
2.2.2
2.2.1
2.2.0
2.1.0
2.0.2
2.0.1
2.0.0
1.2.0
1.1.0
1.0.0
1.0.0-rc.0
retired
0.3.3
retired
0.3.2
retired
0.3.1
retired
0.3.0
retired
0.2.2
retired
0.2.1
retired
0.2.0
retired
0.1.3
retired
0.1.2
retired
0.1.1
retired
0.1.0
List formatting functions for the Common Locale Data Repository (CLDR) package ex_cldr.
Current section
Files
Jump to
Current section
Files
lib/cldr/list/backend.ex
defmodule Cldr.List.Backend do
def define_list_module(config) do
module = inspect(__MODULE__)
backend = config.backend
config = Macro.escape(config)
quote location: :keep, bind_quoted: [module: module, backend: backend, config: config] do
defmodule List do
@moduledoc false
if Cldr.Config.include_module_docs?(config.generate_docs) do
@moduledoc """
Cldr backend module that formats lists.
If we have a list of days like `["Monday", "Tuesday", "Wednesday"]`
then we can format that list for a given locale by:
iex> #{inspect __MODULE__}.to_string(["Monday", "Tuesday", "Wednesday"], locale: "en")
{:ok, "Monday, Tuesday, and Wednesday"}
"""
end
@default_format :standard
alias Cldr.Substitution
alias Cldr.Locale
@doc """
Formats a list into a string according to the list pattern rules for a locale.
## Arguments
* `list` is any list of of terms that can be passed through `Kernel.to_string/1`
* `options` is a keyword list
## Options
* `:locale` is any configured locale. See . The default
is `#{inspect backend}.known_locale_names/0`.
* `:format` is any of those returned by
`Cldr.List.known_list_formats/0` or by `Cldr.List.Pattern.new/1`.
The default is `format: :standard`.
* `:treat_middle_as_end` is a boolean value indicating whether, when
formatting the last element of a list, to use the `:end` pattern (which
typically includes a localized " and " pattern) or to use the `:middle`
pattern which does not include such a pattern. The default is `false`
meaning "use the :end pattern". Note that this option is effective
only when the list contains three or more element.
## Examples
iex> #{inspect __MODULE__}.to_string(["a", "b", "c"], locale: "en")
{:ok, "a, b, and c"}
iex> #{inspect __MODULE__}.to_string(["a", "b", "c"], locale: "en", format: :unit_narrow)
{:ok, "a b c"}
iex> #{inspect __MODULE__}.to_string(["a", "b", "c"], locale: "fr")
{:ok, "a, b et c"}
iex> #{inspect __MODULE__}.to_string([1,2,3,4,5,6])
{:ok, "1, 2, 3, 4, 5, and 6"}
iex> #{inspect __MODULE__}.to_string(["a"])
{:ok, "a"}
iex> #{inspect __MODULE__}.to_string([1,2])
{:ok, "1 and 2"}
"""
@spec to_string(list(term()), Keyword.t()) ::
{:ok, String.t()} | {:error, {module(), String.t()}}
def to_string(list, options \\ []) do
with {:ok, list} <- intersperse(list, options) do
string =
list
|> Enum.map(&to_string/1)
|> :erlang.iolist_to_binary
{:ok, string}
end
end
@doc """
Formats a list using `to_string/2` but raises if there is
an error.
## Examples
iex> #{inspect __MODULE__}.to_string!(["a", "b", "c"], locale: "en")
"a, b, and c"
iex> #{inspect __MODULE__}.to_string!(["a", "b", "c"], locale: "en", format: :unit_narrow)
"a b c"
"""
@spec to_string!(list(term()), Keyword.t()) :: String.t() | no_return()
def to_string!(list, options \\ []) do
case to_string(list, options) do
{:error, {exception, message}} ->
raise exception, message
{:ok, string} ->
string
end
end
@doc """
Intersperses a list elements into a list format according to the list
pattern rules for a locale.
This function can be helpful when creating a list from `Phoenix`
safe strings which are of the format `{:safe, "some string"}`
## Arguments
* `list` is any list of of terms that can be passed through `Kernel.to_string/1`
* `options` is a keyword list
## Options
* `:locale` is any configured locale. See . The default
is `#{inspect backend}.known_locale_names/0`.
* `:format` is any of those returned by
`Cldr.List.known_list_formats/0` or by `Cldr.List.Pattern.new/1`.
The default is `format: :standard`.
* `:treat_middle_as_end` is a boolean value indicating whether, when
formatting the last element of a list, to use the `:end` pattern (which
typically includes a localized " and " pattern) or to use the `:middle`
pattern which does not include such a pattern. The default is `false`
meaning "use the :end pattern". Note that this option is effective
only when the list contains three or more element.
## Examples
iex> #{inspect __MODULE__}.intersperse(["a", "b", "c"], locale: "en")
{:ok, ["a", ", ", "b", ", and ", "c"]}
iex> #{inspect __MODULE__}.intersperse(["a", "b", "c"], locale: "en", format: :unit_narrow)
{:ok, ["a", " ", "b", " ", "c"]}
iex> #{inspect __MODULE__}.intersperse(["a", "b", "c"], locale: "fr")
{:ok, ["a", ", ", "b", " et ", "c"]}
iex> #{inspect __MODULE__}.intersperse([1,2,3,4,5,6])
{:ok, [1, ", ", 2, ", ", 3, ", ", 4, ", ", 5, ", and ", 6]}
iex> #{inspect __MODULE__}.intersperse(["a"])
{:ok, ["a"]}
iex> #{inspect __MODULE__}.intersperse([1,2])
{:ok, [1, " and ", 2]}
iex> #{inspect __MODULE__}.intersperse([1,2], treat_middle_as_end: true)
{:ok, [1, ", ", 2]}
iex> #{inspect __MODULE__}.intersperse([1,2], treat_middle_as_end: false)
{:ok, [1, " and ", 2]}
"""
@spec intersperse(list(term()), Keyword.t()) ::
{:ok, list()} | {:error, {module(), String.t()}}
def intersperse(list, options \\ [])
def intersperse([], _options) do
{:ok, []}
end
def intersperse(list, options) do
with {:ok, locale, pattern, middle_as_end?} <- normalize_options(options) do
list =
list
|> intersperse(locale, pattern, middle_as_end?)
|> Elixir.List.flatten()
{:ok, list}
end
end
# For when the list is empty
defp intersperse([], _locale, _pattern, _middle_as_end?) do
[]
end
# For when there is one element only
defp intersperse([first], _locale, _pattern, _middle_as_end?) do
[first]
end
# For when there are two elements only
defp intersperse([first, last], locale, pattern, false = middle_as_end?) do
Substitution.substitute([first, last], pattern.two)
end
defp intersperse([first, last], locale, pattern, true = middle_as_end?) do
Substitution.substitute([first, last], pattern.start)
end
# For when there are three elements only
defp intersperse([first, middle, last], locale, pattern, false = middle_as_end?) do
last = Substitution.substitute([middle, last], pattern.end)
Substitution.substitute([first, last], pattern.start)
end
defp intersperse([first, middle, last], locale, pattern, true = middle_as_end?) do
last = Substitution.substitute([middle, last], pattern.middle)
Substitution.substitute([first, last], pattern.start)
end
# For when there are more than 3 elements
defp intersperse([first | rest], locale, pattern, middle_as_end?) do
remaining = intersperse(rest, locale, pattern, middle_as_end?)
Substitution.substitute([first, remaining], pattern.start)
end
@doc """
Formats a list using `intersperse/2` but raises if there is
an error.
## Examples
iex> #{inspect __MODULE__}.intersperse!(["a", "b", "c"], locale: "en")
["a", ", ", "b", ", and ", "c"]
iex> #{inspect __MODULE__}.intersperse!(["a", "b", "c"], locale: "en", format: :unit_narrow)
["a", " ", "b", " ", "c"]
"""
@spec intersperse!(list(term()), Keyword.t()) :: list(String.t()) | no_return()
def intersperse!(list, options \\ []) do
case intersperse(list, options) do
{:error, {exception, message}} ->
raise exception, message
{:ok, list} ->
list
end
end
@spec normalize_options(Keyword.t()) ::
{:ok, LanguageTag.t(), Cldr.List.Pattern.t(), boolean()} | {:error, {module(), String.t()}}
defp normalize_options(options) do
locale = options[:locale] || unquote(backend).get_locale()
format = options[:format] || options[:style] || @default_format
middle_as_end? = !!options[:treat_middle_as_end]
with {:ok, locale} <- unquote(backend).validate_locale(locale),
{:ok, pattern} <- verify_format(locale.cldr_locale_name, format) do
{:ok, locale, pattern, middle_as_end?}
end
end
@spec verify_format(Locale.locale_name(), atom() | Cldr.List.Pattern.t()) ::
{:ok, Cldr.List.Pattern.t()} | {:error, {module(), String.t()}}
defp verify_format(_locale_name, %Cldr.List.Pattern{} = pattern) do
{:ok, pattern}
end
defp verify_format(locale_name, format) do
if pattern = Map.get(list_patterns_for(locale_name), format) do
{:ok, pattern}
else
{:error,
{Cldr.UnknownFormatError, "The list format #{inspect(format)} is not known."}}
end
end
@spec list_patterns_for(Locale.locale_name()) :: map() | {:error, {module, String.t()}}
@spec list_formats_for(Locale.locale_name()) :: [atom] | {:error, {module, String.t()}}
for locale_name <- Cldr.Locale.Loader.known_locale_names(config) do
patterns =
locale_name
|> Cldr.Locale.Loader.get_locale(config)
|> Map.get(:list_formats)
|> Enum.map(fn {k, v} ->
patterns =
Cldr.List.Pattern
|> struct(v)
|> Map.put(:two, Map.fetch!(v, 2))
{k, patterns}
end)
|> Map.new()
pattern_names =
Map.keys(patterns)
|> Enum.sort()
@doc """
Returns the list patterns for a locale.
List patterns provide rules for combining multiple
items into a language format appropriate for a locale.
## Example
iex> #{inspect __MODULE__}.list_patterns_for(:en)
%{
or: %Cldr.List.Pattern{
two: [0, " or ", 1],
end: [0, ", or ", 1],
middle: [0, ", ", 1],
start: [0, ", ", 1]
},
or_narrow: %Cldr.List.Pattern{
two: [0, " or ", 1],
end: [0, ", or ", 1],
middle: [0, ", ", 1],
start: [0, ", ", 1]
},
or_short: %Cldr.List.Pattern{
two: [0, " or ", 1],
end: [0, ", or ", 1],
middle: [0, ", ", 1],
start: [0, ", ", 1]
},
standard: %Cldr.List.Pattern{
two: [0, " and ", 1],
end: [0, ", and ", 1],
middle: [0, ", ", 1],
start: [0, ", ", 1]
},
standard_narrow: %Cldr.List.Pattern{
two: [0, ", ", 1],
end: [0, ", ", 1],
middle: [0, ", ", 1],
start: [0, ", ", 1]
},
standard_short: %Cldr.List.Pattern{
two: [0, " & ", 1],
end: [0, ", & ", 1],
middle: [0, ", ", 1],
start: [0, ", ", 1]
},
unit: %Cldr.List.Pattern{
two: [0, ", ", 1],
end: [0, ", ", 1],
middle: [0, ", ", 1],
start: [0, ", ", 1]
},
unit_narrow: %Cldr.List.Pattern{
two: [0, " ", 1],
end: [0, " ", 1],
middle: [0, " ", 1],
start: [0, " ", 1]
},
unit_short: %Cldr.List.Pattern{
two: [0, ", ", 1],
end: [0, ", ", 1],
middle: [0, ", ", 1],
start: [0, ", ", 1]
}
}
"""
def list_patterns_for(unquote(locale_name)) do
unquote(Macro.escape(patterns))
end
@doc """
Returns the styles of list patterns available for a locale.
Returns a list of `atom`s of of the list formats that are
available in CLDR for a locale.
## Example
iex> #{inspect __MODULE__}.list_formats_for(:en)
[:or, :or_narrow, :or_short, :standard, :standard_narrow, :standard_short,
:unit, :unit_narrow, :unit_short]
"""
def list_formats_for(unquote(locale_name)) do
unquote(pattern_names)
end
end
def list_patterns_for(locale_name) do
with {:ok, locale} <- unquote(backend).validate_locale(locale_name) do
list_patterns_for(locale.cldr_locale_name)
end
end
def list_formats_for(locale_name) do
with {:ok, locale} <- unquote(backend).validate_locale(locale_name) do
list_formats_for(locale.cldr_locale_name)
end
end
# TODO remove as of version 3.0
# @deprecated "Use #{__MODULE__}.list_formats_for/1"
@doc false
defdelegate list_styles_for(locale), to: __MODULE__, as: :list_formats_for
# TODO remove as of version 3.0
# @deprecated "Use #{__MODULE__}.list_formats_for/1"
@doc false
defdelegate list_pattern_styles_for(locale), to: __MODULE__, as: :list_formats_for
end
end
end
end