Packages
ex_cldr
0.8.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.
Retired package: Deprecated
Current section
Files
Jump to
Current section
Files
lib/cldr/accept_language.ex
defmodule Cldr.AcceptLanguage do
@moduledoc """
Tokenizer and parser for HTTP `Accept-Language` header values as defined in
[rfc2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4).
The Accept-Language request-header field is similar to Accept, but restricts
the set of natural languages that are preferred as a response to the request.
Language tags function are provided in `Cldr.LanguageTag`.
The format of an `Accept-Language` header is as follows in `ABNF` format:
Accept-Language = "Accept-Language" ":"
1#( language-range [ ";" "q" "=" qvalue ] )
language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" )
Each language-range MAY be given an associated quality value which represents an
estimate of the user's preference for the languages specified by that range. The
quality value defaults to "q=1". For example,
Accept-Language: da, en-gb;q=0.8, en;q=0.7
would mean: "I prefer Danish, but will accept British English and other types of English."
"""
alias Cldr.Locale
@default_quality 1.0
@low_quality 0.2
@doc """
Splits the language ranges for an `Accept-Language` header
value into tuples `{quality, language}`.
## Example
iex> Cldr.AcceptLanguage.tokenize "da,zh-TW;q=0.3"
[{1.0, "da"}, {0.3, "zh-tw"}]
"""
@language_separator Regex.compile! ","
def tokenize(accept_language) do
accept_language
|> String.downcase
|> remove_whitespace
|> String.split(@language_separator)
|> Enum.reject(&is_nil/1)
|> Enum.reject(&String.starts_with?(&1, "*"))
|> Enum.map(&token_tuple/1)
end
@quality_separator ";q="
defp token_tuple(language) do
case String.split(language, @quality_separator) do
[language, quality] ->
{parse_quality(quality), language}
[language] ->
{@default_quality, language}
[language | _rest] ->
{@low_quality, language}
end
end
@doc """
Parses an `Accept-Language` header value in its string
or tokenized form to return a tuple of the form
`{:ok, [{quality, %Cldr.LanguageTag{}}, ...]}` sorted by quality.
If not valid language tags are found, the tuple `{:error, reason}`
is returned.
If at least one valid language tag is found but errors are also
detected on one more more tags, an `{ok, list}` tuple is returned
wuth an error tuple for each invalid tag added at the end of the list.
## Example
iex> Cldr.AcceptLanguage.parse "da,zh-TW;q=0.3"
{:ok,
[{1.0,
%Cldr.LanguageTag{canonical_locale_name: "da-Latn-DK",
cldr_locale_name: "da", extensions: %{}, language: "da",
locale: [], private_use: [], rbnf_locale_name: "da",
region: "DK", requested_locale_name: "da", script: "Latn",
transform: %{}, variant: nil}},
{0.3,
%Cldr.LanguageTag{canonical_locale_name: "zh-Hant-TW",
cldr_locale_name: "zh-Hant", extensions: %{}, language: "zh",
locale: [], private_use: [], rbnf_locale_name: "zh-Hant",
region: "TW", requested_locale_name: "zh-TW", script: "Hant",
transform: %{}, variant: nil}}]}
iex> Cldr.AcceptLanguage.parse "X"
{:error,
{Cldr.AcceptLanguageError,
"Could not parse language tag. Error was detected at 'x'"}}
iex> Cldr.AcceptLanguage.parse "da,zh-TW;q=0.3,X"
{:ok,
[{1.0,
%Cldr.LanguageTag{canonical_locale_name: "da-Latn-DK",
cldr_locale_name: "da", extensions: %{}, language: "da",
locale: [], private_use: [], rbnf_locale_name: "da",
region: "DK", requested_locale_name: "da", script: "Latn",
transform: %{}, variant: nil}},
{0.3,
%Cldr.LanguageTag{canonical_locale_name: "zh-Hant-TW",
cldr_locale_name: "zh-Hant", extensions: %{}, language: "zh",
locale: [], private_use: [], rbnf_locale_name: "zh-Hant",
region: "TW", requested_locale_name: "zh-TW", script: "Hant",
transform: %{}, variant: nil}},
{:error,
{Cldr.InvalidLanguageTag,
"Could not parse language tag. Error was detected at 'x'"},
"x"}]}
"""
def parse(tokens) when is_list(tokens) do
accept_language =
tokens
|> parse_language_tags
|> sort_by_quality
case accept_language do
[{:error, reason, language_tag}] ->
{:error, accept_language_error(reason, language_tag)}
_ ->
{:ok, accept_language}
end
end
def parse(string) when is_binary(string) do
string
|> tokenize
|> parse
end
@doc """
Parses an `Accept-Language` header value in its string
or tokenized form to produce a list of tuples of the form
`[{quality, %Cldr.LanguageTag{}}, ...]` sorted by quality
in decending order.
If no valid language tags are found, an exception is raised.
If at least one valid language tag is found but errors are also
detected on one more more tags, an `{ok, list}` tuple is returned
wuth an error tuple for each invalid tag added at the end of the list.
## Example
iex> Cldr.AcceptLanguage.parse! "da,zh-TW;q=0.3"
[{1.0,
%Cldr.LanguageTag{canonical_locale_name: "da-Latn-DK",
cldr_locale_name: "da", extensions: %{}, language: "da",
locale: [], private_use: [], rbnf_locale_name: "da", region: "DK",
requested_locale_name: "da", script: "Latn", transform: %{},
variant: nil}},
{0.3,
%Cldr.LanguageTag{canonical_locale_name: "zh-Hant-TW",
cldr_locale_name: "zh-Hant", extensions: %{}, language: "zh",
locale: [], private_use: [], rbnf_locale_name: "zh-Hant",
region: "TW", requested_locale_name: "zh-TW", script: "Hant",
transform: %{}, variant: nil}}]
Cldr.AcceptLanguage.parse! "X"
** (Cldr.AcceptLanguageError) Could not parse language tag. Error was detected at 'x'
(ex_cldr) lib/cldr/accept_language.ex:168: Cldr.AcceptLanguage.parse!/1
iex> Cldr.AcceptLanguage.parse! "da,zh-TW;q=0.3,X"
[{1.0,
%Cldr.LanguageTag{canonical_locale_name: "da-Latn-DK",
cldr_locale_name: "da", extensions: %{}, language: "da",
locale: [], private_use: [], rbnf_locale_name: "da", region: "DK",
requested_locale_name: "da", script: "Latn", transform: %{},
variant: nil}},
{0.3,
%Cldr.LanguageTag{canonical_locale_name: "zh-Hant-TW",
cldr_locale_name: "zh-Hant", extensions: %{}, language: "zh",
locale: [], private_use: [], rbnf_locale_name: "zh-Hant",
region: "TW", requested_locale_name: "zh-TW", script: "Hant",
transform: %{}, variant: nil}},
{:error,
{Cldr.InvalidLanguageTag,
"Could not parse language tag. Error was detected at 'x'"}, "x"}]
"""
def parse!(accept_language) do
case parse(accept_language) do
{:ok, parse_result} -> parse_result
{:error, {exception, reason}} -> raise exception, reason
end
end
@doc """
Filters the returned results of `parse/1` to return
only the error tuples.
## Example
iex> Cldr.AcceptLanguage.parse!("da,zh-TW;q=0.3,X") |> Cldr.AcceptLanguage.errors
[{:error,
{Cldr.InvalidLanguageTag,
"Could not parse language tag. Error was detected at 'x'"}, "x"}]
"""
def errors(parse_result) when is_list(parse_result) do
Enum.filter(parse_result, fn
{:error, _, _} -> true
_ -> false
end)
end
defp parse_quality(quality_string) do
case Float.parse(quality_string) do
:error -> @low_quality
{quality, _} -> quality
end
end
defp parse_language_tags(tokens) do
Enum.map(tokens, fn {quality, language_tag} ->
case Locale.canonical_language_tag(language_tag) do
{:ok, tag} ->
{quality, tag}
{:error, reason} ->
{:error, reason, language_tag}
end
end)
end
defp remove_whitespace(accept_language) do
String.replace(accept_language, " ", "")
end
defp sort_by_quality(tokens) do
Enum.sort(tokens, fn
{:error, _, _}, {_quality_2, _} -> false
{_quality_2, _}, {:error, _, _} -> true
{quality_1, _}, {quality_2, _} -> quality_1 > quality_2
end)
end
defp accept_language_error({_exception, reason}, _language_tag) do
{Cldr.AcceptLanguageError, reason}
end
end