From a94df7792240dca3646f259489934ec7da244550 Mon Sep 17 00:00:00 2001 From: c4710n Date: Sat, 30 Dec 2023 18:43:42 +0800 Subject: [PATCH] feat: rename `:sanitize_locale_by` option `:cast_locale_by` --- CHANGELOG.md | 8 ++- lib/plug_locale/header.ex | 70 ++++++++++++++++++++++----- lib/plug_locale/header/config.ex | 11 ++--- lib/plug_locale/sanitizer.ex | 50 ------------------- lib/plug_locale/web_browser.ex | 49 +++++++++++++++++-- lib/plug_locale/web_browser/config.ex | 11 ++--- 6 files changed, 119 insertions(+), 80 deletions(-) delete mode 100644 lib/plug_locale/sanitizer.ex diff --git a/CHANGELOG.md b/CHANGELOG.md index 0324ea5..1cc7dcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,13 @@ # Changelog +## Unreleased + +Breaking changes: + +- rename `:sanitize_locale_by` option `:cast_locale_by` + ## v0.3.0 Breaking changes: -- `:sanitize_locale` is renamed to `:sanitize_locale_by` +- rename `:sanitize_locale` option `:sanitize_locale_by` diff --git a/lib/plug_locale/header.ex b/lib/plug_locale/header.ex index c36d0b8..0a56658 100644 --- a/lib/plug_locale/header.ex +++ b/lib/plug_locale/header.ex @@ -62,14 +62,50 @@ defmodule PlugLocale.Header do * `:default_locale` - the default locale. * `:locales` - all the supported locales. Default to `[]`. - * `:sanitize_locale_by` - specify the function for sanitizing extracted or - detected locales. Default to `&PlugLocale.Sanitizer.sanitize/1` which does - nothing. See `PlugLocale.Sanitizer` for more details. + * `:cast_locale_by` - specify the function for casting extracted or + detected locales. Default to `nil`. * `:header_name` - the header for getting locale. Default to `"x-client-locale"`. * `:assign_key` - the key for putting value into `assigns` storage. Default to `:locale`. + ### about `:cast_locale_by` option + + By default, the value is `nil`, which means doing nothing. But, in practice, + you will need to use something meaningful. + + A possible implementation: + + defmodule DemoWeb.I18n do + def cast_locale(locale) do + case locale do + # explicit matching on supported locales + locale when locale in ["en", "zh"] -> + locale + + # fuzzy matching on en locale + "en-" <> _ -> + "en" + + # fuzzy matching on zh locale + "zh-" <> _ -> + "zh" + + # fallback for unsupported locales + _ -> + "en" + end + end + end + + Then, use above implementation for plug: + + plug `#{inspect(__MODULE__)}`, + default_locale: "en", + locales: ["en", "zh"], + sanitize_locale_by: &DemoWeb.I18n.cast_locale/1, + # ... + """ @behaviour Plug @@ -83,16 +119,9 @@ defmodule PlugLocale.Header do @impl true def call(conn, config) do locale = get_locale_from_header(conn, config) + casted_locale = cast_locale(config, locale, default: config.default_locale) - locale = - if locale do - locale = config.sanitize_locale_by.(locale) - if locale in config.locales, do: locale, else: config.default_locale - else - config.default_locale - end - - assign(conn, config.assign_key, locale) + assign(conn, config.assign_key, casted_locale) end defp get_locale_from_header(conn, config) do @@ -101,4 +130,21 @@ defmodule PlugLocale.Header do _ -> nil end end + + defp cast_locale(config, locale, opts \\ []) do + default = Keyword.get(opts, :default, nil) + + if locale do + casted_locale = + if is_function(config.cast_locale_by, 1), + do: config.cast_locale_by.(locale), + else: locale + + if casted_locale in config.locales, + do: casted_locale, + else: default + else + default + end + end end diff --git a/lib/plug_locale/header/config.ex b/lib/plug_locale/header/config.ex index 419012b..8223bca 100644 --- a/lib/plug_locale/header/config.ex +++ b/lib/plug_locale/header/config.ex @@ -4,7 +4,7 @@ defmodule PlugLocale.Header.Config do defstruct [ :default_locale, :locales, - :sanitize_locale_by, + :cast_locale_by, :header_name, :assign_key ] @@ -13,18 +13,17 @@ defmodule PlugLocale.Header.Config do def new!(opts) when is_list(opts) do default_locale = Keyword.get(opts, :default_locale) locales = Keyword.get(opts, :locales, []) - sanitize_locale_by = Keyword.get(opts, :sanitize_locale_by, &PlugLocale.Sanitizer.sanitize/1) + cast_locale_by = Keyword.get(opts, :cast_locale_by, nil) header_name = Keyword.get(opts, :header_name, "x-client-locale") assign_key = Keyword.get(opts, :assign_key, :locale) [ default_locale: default_locale, locales: locales, - sanitize_locale_by: sanitize_locale_by, + cast_locale_by: cast_locale_by, header_name: header_name, assign_key: assign_key ] - |> validate!() |> as_map!() |> as_struct!() end @@ -43,14 +42,14 @@ defmodule PlugLocale.Header.Config do defp as_map!(opts) do default_locale = Keyword.fetch!(opts, :default_locale) locales = Keyword.fetch!(opts, :locales) - sanitize_locale_by = Keyword.fetch!(opts, :sanitize_locale_by) + cast_locale_by = Keyword.fetch!(opts, :cast_locale_by) header_name = Keyword.fetch!(opts, :header_name) assign_key = Keyword.fetch!(opts, :assign_key) %{ default_locale: default_locale, locales: Enum.uniq([default_locale | locales]), - sanitize_locale_by: sanitize_locale_by, + cast_locale_by: cast_locale_by, header_name: header_name, assign_key: assign_key } diff --git a/lib/plug_locale/sanitizer.ex b/lib/plug_locale/sanitizer.ex deleted file mode 100644 index 23b6e02..0000000 --- a/lib/plug_locale/sanitizer.ex +++ /dev/null @@ -1,50 +0,0 @@ -defmodule PlugLocale.Sanitizer do - @moduledoc """ - The default implementation is a function like `fn x -> x end`, which does - nothing. But, in practice, you will need to use something meaningful. - - A possible implementation: - - defmodule DemoWeb.LocaleSanitizer do - def sanitize(locale) do - case locale do - # explicit matching on supported locales - locale when locale in ["en", "zh"] -> - locale - - # fuzzy matching on en locale - "en-" <> _ -> - "en" - - # fuzzy matching on zh locale - "zh-" <> _ -> - "zh" - - # fallback for unsupported locales - _ -> - "en" - end - end - end - - Then, use above implementation in plugs by using `:sanitize_locale_by` option: - - # use it for PlugLocale.WebBrowser - plug PlugLocale.WebBrowser, - default_locale: "en", - locales: ["en", "zh"], - sanitize_locale_by: &DemoWeb.LocaleSanitizer.sanitize/1, - # ... - - # use it for PlugLocale.Header - plug PlugLocale.Header, - default_locale: "en", - locales: ["en", "zh"], - sanitize_locale_by: &DemoWeb.LocaleSanitizer.sanitize/1, - # ... - - """ - - @doc false - def sanitize(locale), do: locale -end diff --git a/lib/plug_locale/web_browser.ex b/lib/plug_locale/web_browser.ex index 9786eb4..027154f 100644 --- a/lib/plug_locale/web_browser.ex +++ b/lib/plug_locale/web_browser.ex @@ -87,12 +87,11 @@ defmodule PlugLocale.WebBrowser do * `:default_locale` - the default locale. * `:locales` - all the supported locales. Default to `[]`. * `:detect_locale_from` - specify *the sources* and *the order of sources* - for detecting locale. + for detecting locale. Available sources are `:query`, `:cookie`, `:referrer`, `:accept_language`. Default to `[:cookie, :referrer, :accept_language]`. - * `:sanitize_locale_by` - specify the function for sanitizing extracted or - detected locales. Default to `&PlugLocale.Sanitizer.sanitize/1` which does - nothing. See `PlugLocale.Sanitizer` for more details. + * `:cast_locale_by` - specify the function for casting extracted or + detected locales. Default to `nil`. * `:route_identifier` - the part for identifying locale in route. Default to `:locale`. * `:assign_key` - the key for putting value into `assigns` storage. @@ -102,6 +101,43 @@ defmodule PlugLocale.WebBrowser do * `:cookie_key` - the key for getting locale from cookie. Default to `"preferred_locale"`. + ### about `:cast_locale_by` option + + By default, the value is `nil`, which means doing nothing. But, in practice, + you will need to use something meaningful. + + A possible implementation: + + defmodule DemoWeb.I18n do + def cast_locale(locale) do + case locale do + # explicit matching on supported locales + locale when locale in ["en", "zh"] -> + locale + + # fuzzy matching on en locale + "en-" <> _ -> + "en" + + # fuzzy matching on zh locale + "zh-" <> _ -> + "zh" + + # fallback for unsupported locales + _ -> + "en" + end + end + end + + Then, use above implementation for plug: + + plug `#{inspect(__MODULE__)}`, + default_locale: "en", + locales: ["en", "zh"], + cast_locale_by: &DemoWeb.I18n.cast_locale/1, + # ... + ## Helper functions `#{inspect(__MODULE__)}` also provides some helper functions, which will be useful @@ -426,7 +462,10 @@ defmodule PlugLocale.WebBrowser do default = Keyword.get(opts, :default, nil) if locale do - casted_locale = config.sanitize_locale_by.(locale) + casted_locale = + if is_function(config.cast_locale_by, 1), + do: config.cast_locale_by.(locale), + else: locale if casted_locale in config.locales, do: casted_locale, diff --git a/lib/plug_locale/web_browser/config.ex b/lib/plug_locale/web_browser/config.ex index c915f38..dba9a6d 100644 --- a/lib/plug_locale/web_browser/config.ex +++ b/lib/plug_locale/web_browser/config.ex @@ -5,7 +5,7 @@ defmodule PlugLocale.WebBrowser.Config do :default_locale, :locales, :detect_locale_from, - :sanitize_locale_by, + :cast_locale_by, :route_identifier, :path_param_key, :assign_key, @@ -21,7 +21,7 @@ defmodule PlugLocale.WebBrowser.Config do detect_locale_from = Keyword.get(opts, :detect_locale_from, [:cookie, :referrer, :accept_language]) - sanitize_locale_by = Keyword.get(opts, :sanitize_locale_by, &PlugLocale.Sanitizer.sanitize/1) + cast_locale_by = Keyword.get(opts, :cast_locale_by, nil) route_identifier = Keyword.get(opts, :route_identifier, :locale) assign_key = Keyword.get(opts, :assign_key, route_identifier) query_key = Keyword.get(opts, :query_key, to_string(route_identifier)) @@ -31,13 +31,12 @@ defmodule PlugLocale.WebBrowser.Config do default_locale: default_locale, locales: locales, detect_locale_from: detect_locale_from, - sanitize_locale_by: sanitize_locale_by, + cast_locale_by: cast_locale_by, route_identifier: route_identifier, assign_key: assign_key, query_key: query_key, cookie_key: cookie_key ] - |> validate!() |> as_map!() |> as_struct!() end @@ -57,7 +56,7 @@ defmodule PlugLocale.WebBrowser.Config do default_locale = Keyword.fetch!(opts, :default_locale) locales = Keyword.fetch!(opts, :locales) detect_locale_from = Keyword.fetch!(opts, :detect_locale_from) - sanitize_locale_by = Keyword.fetch!(opts, :sanitize_locale_by) + cast_locale_by = Keyword.fetch!(opts, :cast_locale_by) route_identifier = Keyword.fetch!(opts, :route_identifier) assign_key = Keyword.fetch!(opts, :assign_key) query_key = Keyword.fetch!(opts, :query_key) @@ -67,7 +66,7 @@ defmodule PlugLocale.WebBrowser.Config do default_locale: default_locale, locales: Enum.uniq([default_locale | locales]), detect_locale_from: detect_locale_from, - sanitize_locale_by: sanitize_locale_by, + cast_locale_by: cast_locale_by, route_identifier: route_identifier, path_param_key: to_string(route_identifier), assign_key: assign_key,