Packages
ex_cldr
2.17.0
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
Current section
Files
lib/cldr/plug/plug_set_locale.ex
if Code.ensure_loaded?(Plug) do
defmodule Cldr.Plug.SetLocale do
@moduledoc """
Sets the Cldr and/or Gettext locales derived from the accept-language
header, a query parameter, a url parameter, a body parameter or the
session.
## Options
* `:apps` - list of apps for which to set locale.
See the apps configuration section.
* `:from` - where in the request to look for the locale.
The default is `[:session, :accept_language]`. The valid
options are:
* `:accept_language` will parse the `accept-language` header
and finds the best matched configured locale
* `:path` will look for a locale by examining `conn.path_params`
* `:query` will look for a locale by examining `conn.query_params`
* `:body` will look for a locale by examining `conn.body_params`
* `:cookie` will look for a locale in the request cookie(s)
* `:session` will look for a locale in the session
* `:default` - the default locale to set if no locale is
found by other configured methods. It can be a string like "en"
or a `Cldr.LanguageTag` struct. The default is
`Cldr.default_locale/1`
* `:gettext` - the name of the `Gettext` backend module upon which
the locale will be validated. This option is not required if a
gettext module is specified in the `:apps` configuration.
* `:cldr` - the name of the `Cldr` backend module upon which
the locale will be validated. This option is not required if a
gettext module is specified in the `:apps` configuration.
* `:session_key` - defines the key used to look for the locale
in the session. The default is `locale`.
If a locale is found then `conn.private[:cldr_locale]` is also set.
It can be retrieved with `Cldr.Plug.SetLocale.get_cldr_locale/1`.
## App configuration
The `:apps` configuration key defines which applications will have
their locale *set* by this plug.
`Cldr.Plug.SetLocale` can set the locale for `cldr`, `gettext` or both.
The basic configuration of the `:app` key is an atom, or list of atoms,
containing one or both of these app names. For example:
apps: :cldr
apps: :gettext
apps: [:cldr, :gettext]
In each of these cases, the locale is set globally
**for the current process**.
Sometimes setting the locale for only a specific backend is required.
In this case, configure the `:apps` key as a keyword list pairing an
application with the required backend module. The value `:global` signifies
setting the local for the global context. For example:
apps: [cldr: MyApp.Cldr]
apps: [gettext: MyAppGettext]
apps: [gettext: :global]
apps: [cldr: MyApp.Cldr, gettext: MyAppGettext]
## Using Cldr.Plug.SetLocale without Phoenix
If you are using `Cldr.Plug.SetLocale` without Phoenix and you
plan to use `:path_param` to identify the locale of a request
then `Cldr.Plug.SetLocale` must be configured *after* `plug :match`
and *before* `plug :dispatch`. For example:
defmodule MyRouter do
use Plug.Router
plug :match
plug Cldr.Plug.SetLocale,
apps: [:cldr, :gettext],
from: [:path, :query],
gettext: MyApp.Gettext,
cldr: MyApp.Cldr
plug :dispatch
get "/hello/:locale" do
send_resp(conn, 200, "world")
end
end
## Using Cldr.Plug.SetLocale with Phoenix
If you are using `Cldr.Plug.SetLocale` with Phoenix and you plan
to use the `:path_param` to identify the locale of a request then
`Cldr.Plug.SetLocale` must be configured in the router module, *not*
in the endpoint module. This is because `conn.path_params` has
not yet been populated in the endpoint. For example:
defmodule MyAppWeb.Router do
use MyAppWeb, :router
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug Cldr.Plug.SetLocale,
apps: [:cldr, :gettext],
from: [:path, :query],
gettext: MyApp.Gettext,
cldr: MyApp.Cldr
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
end
scope "/:locale", HelloWeb do
pipe_through :browser
get "/", PageController, :index
end
end
## Examples
# Will set the global locale for the current process
# for both `:cldr` and `:gettext`
plug Cldr.Plug.SetLocale,
apps: [:cldr, :gettext],
from: [:query, :path, :body, :cookie, :accept_language],
param: "locale",
gettext: GetTextModule,
cldr: MyApp.Cldr
session_key: "cldr_locale"
# Will set the backend-only locale for the current process
# for both `:cldr` and `:gettext`
plug Cldr.Plug.SetLocale,
apps: [cldr: MyApp.Cldr, gettext: GetTextModule],
from: [:query, :path, :body, :cookie, :accept_language],
param: "locale",
session_key: "cldr_locale"
# Will set the backend-only locale for the current process
# for `:cldr` and globally for `:gettext`
plug Cldr.Plug.SetLocale,
apps: [cldr: MyApp.Cldr, gettext: :global],
from: [:query, :path, :body, :cookie, :accept_language],
param: "locale",
session_key: "cldr_locale"
"""
import Plug.Conn
require Logger
alias Cldr.AcceptLanguage
@default_apps [cldr: :global]
@default_from [:session, :accept_language]
@default_param_name "locale"
@default_session_key "cldr_locale"
@from_options [:accept_language, :path, :body, :query, :session, :cookie]
@app_options [:cldr, :gettext]
@language_header "accept-language"
@doc false
def init(options) do
options
|> validate_apps(options[:apps])
|> validate_from(options[:from])
|> validate_param(options[:param])
|> validate_cldr(options[:cldr])
|> validate_gettext(options[:gettext])
|> validate_default(options[:default])
|> validate_session_key(options[:session_key])
end
@doc false
def call(conn, options) do
if locale = locale_from_params(conn, options[:from], options) || options[:default] do
Enum.each(options[:apps], fn app ->
put_locale(app, locale, options)
end)
end
put_private(conn, :cldr_locale, locale)
end
@doc """
Return the locale set by `Cldr.Plug.SetLocale`
"""
def get_cldr_locale(conn) do
conn.private[:cldr_locale]
end
defp locale_from_params(conn, from, options) do
Enum.reduce_while(from, nil, fn param, _acc ->
conn
|> fetch_param(param, options[:param], options)
|> return_if_valid_locale
end)
end
defp fetch_param(conn, :accept_language, _param, options) do
case get_req_header(conn, @language_header) do
[accept_language] -> AcceptLanguage.best_match(accept_language, options[:cldr])
[accept_language | _] -> AcceptLanguage.best_match(accept_language, options[:cldr])
[] -> nil
end
end
defp fetch_param(
%Plug.Conn{query_params: %Plug.Conn.Unfetched{aspect: :query_params}} = conn,
:query,
param,
options
) do
conn = fetch_query_params(conn)
fetch_param(conn, :query, param, options)
end
defp fetch_param(conn, :query, param, options) do
conn
|> Map.get(:query_params)
|> Map.get(param)
|> Cldr.validate_locale(options[:cldr])
end
defp fetch_param(conn, :path, param, options) do
conn
|> Map.get(:path_params)
|> Map.get(param)
|> Cldr.validate_locale(options[:cldr])
end
defp fetch_param(conn, :body, param, options) do
conn
|> Map.get(:body_params)
|> Map.get(param)
|> Cldr.validate_locale(options[:cldr])
end
defp fetch_param(conn, :session, _param, options) do
conn
|> get_session(options[:session_key])
|> Cldr.validate_locale(options[:cldr])
end
defp fetch_param(conn, :cookie, param, options) do
conn
|> Map.get(:cookies)
|> Map.get(param)
|> Cldr.validate_locale(options[:cldr])
end
defp return_if_valid_locale(nil) do
{:cont, nil}
end
defp return_if_valid_locale({:error, _}) do
{:cont, nil}
end
defp return_if_valid_locale({:ok, locale}) do
{:halt, locale}
end
defp put_locale({:cldr, :global}, locale, _options) do
Cldr.put_locale(locale)
end
# Deprecated option :all. Use :global
defp put_locale({:cldr, :all}, locale, _options) do
Cldr.put_locale(locale)
end
defp put_locale({:cldr, backend}, locale, _options) do
backend.put_locale(locale)
end
defp put_locale({:gettext, _}, %Cldr.LanguageTag{gettext_locale_name: nil} = locale, _options) do
Logger.warn(
"Locale #{inspect(locale.requested_locale_name)} does not have a known " <>
"Gettext locale. No Gettext locale has been set."
)
nil
end
defp put_locale(
{:gettext, :global},
%Cldr.LanguageTag{gettext_locale_name: locale_name},
_options
) do
{:ok, apply(Gettext, :put_locale, [locale_name])}
end
# Deprecated option :all. Use :global
defp put_locale(
{:gettext, :all},
%Cldr.LanguageTag{gettext_locale_name: locale_name},
_options
) do
{:ok, apply(Gettext, :put_locale, [locale_name])}
end
defp put_locale(
{:gettext, backend},
%Cldr.LanguageTag{gettext_locale_name: locale_name},
_options
) do
{:ok, apply(Gettext, :put_locale, [backend, locale_name])}
end
defp validate_apps(options, nil), do: Keyword.put(options, :apps, @default_apps)
defp validate_apps(options, app) when is_atom(app) do
options
|> Keyword.put(:apps, [app])
|> validate_apps([app])
end
defp validate_apps(options, apps) when is_list(apps) do
app_config =
Enum.map(apps, fn
{app, scope} ->
validate_app_and_scope!(app, scope)
{app, scope}
app ->
validate_app_and_scope!(app, nil)
{app, :global}
end)
Keyword.put(options, :apps, app_config)
end
defp validate_apps(_options, apps) do
raise(
ArgumentError,
"Invalid apps list: #{inspect(apps)}."
)
end
defp validate_app_and_scope!(app, nil) when app in @app_options do
:ok
end
defp validate_app_and_scope!(app, :global) when app in @app_options do
:ok
end
# Deprecated option :all. Use :global
defp validate_app_and_scope!(app, :all) when app in @app_options do
:ok
end
defp validate_app_and_scope!(:cldr, module) when is_atom(module) do
Cldr.validate_backend!(module)
:ok
end
defp validate_app_and_scope!(:gettext, module) when is_atom(module) do
Cldr.Code.ensure_compiled?(module) ||
raise(ArgumentError, "Gettext backend #{inspect module} is unknown")
:ok
end
defp validate_app_and_scope!(app, scope) do
raise(
ArgumentError,
"Invalid app #{inspect(app)} or scope #{inspect(scope)} detected."
)
end
defp validate_from(options, nil), do: Keyword.put(options, :from, @default_from)
defp validate_from(options, from) when is_atom(from) do
options
|> Keyword.put(:from, [from])
|> validate_from([from])
end
defp validate_from(options, from) when is_list(from) do
Enum.each(from, fn f ->
if f not in @from_options do
raise(
ArgumentError,
"Invalid :from option #{inspect(f)} detected. " <>
" Valid :from options are #{inspect(@from_options)}"
)
end
end)
options
end
defp validate_from(_options, from) do
raise(
ArgumentError,
"Invalid :from list #{inspect(from)} detected. " <>
"Valid from options are #{inspect(@from_options)}"
)
end
defp validate_param(options, nil), do: Keyword.put(options, :param, @default_param_name)
defp validate_param(options, param) when is_binary(param), do: options
defp validate_param(options, param) when is_atom(param) do
validate_from(options, param)
end
defp validate_param(_options, param) do
raise(
ArgumentError,
"Invalid :param #{inspect(param)} detected. " <> ":param must be a string"
)
end
defp validate_default(options, nil) do
default = options[:cldr].default_locale()
Keyword.put(options, :default, default)
end
defp validate_default(options, default) do
case Cldr.validate_locale(default, options[:cldr]) do
{:ok, locale} -> Keyword.put(options, :default, locale)
{:error, {exception, reason}} -> raise exception, reason
end
end
# No configured gettext. See if there is one configured
# on the Cldr backend
defp validate_gettext(options, nil) do
gettext = options[:cldr].__cldr__(:config).gettext
if gettext && get_in(options, [:apps, :gettext]) do
Keyword.put(options, :gettext, gettext)
else
options
end
end
defp validate_gettext(options, gettext) do
case Code.ensure_compiled(gettext) do
{:error, _} ->
raise ArgumentError, "Gettext module #{inspect(gettext)} is not known"
{:module, _} ->
options
end
end
defp validate_session_key(options, nil),
do: Keyword.put(options, :session_key, @default_session_key)
defp validate_session_key(options, session_key) when is_binary(session_key), do: options
defp validate_session_key(_options, session_key) do
raise(
ArgumentError,
"Invalid :session_key #{inspect(session_key)} detected. " <>
":session_key must be a string"
)
end
defp validate_cldr(options, nil) do
backend = Keyword.get_lazy(options[:apps], :cldr, &Cldr.default_locale/0)
validate_cldr(options, backend)
end
defp validate_cldr(options, backend) when is_atom(backend) do
with {:ok, backend} <- Cldr.validate_backend(backend) do
Keyword.put(options, :cldr, backend)
else
{:error, {exception, reason}} -> raise exception, reason
end
end
end
end