forked from YokaiRick/akkoma
Support multiple locales formally
elixir gettext current does not fully support fallback to another language [0]. But it might in the future. We adapt it so that all languages in Accept-Language headers are received by Pleroma.Web.Gettext. User.languages is now a comma-separated list. [0]: https://github.com/elixir-gettext/gettext/issues/303
This commit is contained in:
parent
845e5769ce
commit
fa95bc8725
3 changed files with 89 additions and 17 deletions
|
@ -65,6 +65,24 @@ def supported_variants_of_locale(locale) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_locales() do
|
||||||
|
Process.get({Pleroma.Web.Gettext, :locales}, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_locale_list(locales) do
|
||||||
|
Enum.all?(locales, &is_binary/1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_locales(locales) do
|
||||||
|
if is_locale_list(locales) do
|
||||||
|
Process.put({Pleroma.Web.Gettext, :locales}, Enum.uniq(locales))
|
||||||
|
Gettext.put_locale(Enum.at(locales, 0, Gettext.get_locale()))
|
||||||
|
:ok
|
||||||
|
else
|
||||||
|
{:error, :not_locale_list}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def locale_or_default(locale) do
|
def locale_or_default(locale) do
|
||||||
if supports_locale?(locale) do
|
if supports_locale?(locale) do
|
||||||
locale
|
locale
|
||||||
|
@ -73,11 +91,46 @@ def locale_or_default(locale) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defmacro with_locale_or_default(locale, do: fun) do
|
def with_locales_func(locales, fun) do
|
||||||
|
prev_locales = Process.get({Pleroma.Web.Gettext, :locales})
|
||||||
|
put_locales(locales)
|
||||||
|
|
||||||
|
try do
|
||||||
|
fun.()
|
||||||
|
after
|
||||||
|
if prev_locales do
|
||||||
|
put_locales(prev_locales)
|
||||||
|
else
|
||||||
|
Process.delete({Pleroma.Web.Gettext, :locales})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defmacro with_locales(locales, do: fun) do
|
||||||
quote do
|
quote do
|
||||||
Gettext.with_locale(Pleroma.Web.Gettext.locale_or_default(unquote(locale)), fn ->
|
Pleroma.Web.Gettext.with_locales_func(unquote(locales), fn ->
|
||||||
unquote(fun)
|
unquote(fun)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def to_locale_list(locale) when is_binary(locale) do
|
||||||
|
locale
|
||||||
|
|> String.split(",")
|
||||||
|
|> Enum.filter(&supports_locale?/1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_locale_list(_), do: []
|
||||||
|
|
||||||
|
defmacro with_locale_or_default(locale, do: fun) do
|
||||||
|
quote do
|
||||||
|
Pleroma.Web.Gettext.with_locales_func(
|
||||||
|
Pleroma.Web.Gettext.to_locale_list(unquote(locale))
|
||||||
|
|> Enum.concat(Pleroma.Web.Gettext.get_locales()),
|
||||||
|
fn ->
|
||||||
|
unquote(fun)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,22 +11,27 @@ def frontend_language_cookie_name, do: "userLanguage"
|
||||||
def init(_), do: nil
|
def init(_), do: nil
|
||||||
|
|
||||||
def call(conn, _) do
|
def call(conn, _) do
|
||||||
locale = get_locale_from_header(conn) || Gettext.get_locale()
|
locales = get_locales_from_header(conn)
|
||||||
Gettext.put_locale(locale)
|
first_locale = Enum.at(locales, 0, Gettext.get_locale())
|
||||||
assign(conn, :locale, locale)
|
|
||||||
|
Pleroma.Web.Gettext.put_locales(locales)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> assign(:locale, first_locale)
|
||||||
|
|> assign(:locales, locales)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_locale_from_header(conn) do
|
defp get_locales_from_header(conn) do
|
||||||
conn
|
conn
|
||||||
|> extract_preferred_language()
|
|> extract_preferred_language()
|
||||||
|> normalize_language_codes()
|
|> normalize_language_codes()
|
||||||
|> first_supported()
|
|> all_supported()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp first_supported(locales) do
|
defp all_supported(locales) do
|
||||||
locales
|
locales
|
||||||
|> Enum.flat_map(&Pleroma.Web.Gettext.supported_variants_of_locale/1)
|
|> Enum.flat_map(&Pleroma.Web.Gettext.supported_variants_of_locale/1)
|
||||||
|> Enum.find(&supported_locale?/1)
|
|> Enum.filter(&supported_locale?/1)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp normalize_language_codes(codes) do
|
defp normalize_language_codes(codes) do
|
||||||
|
|
|
@ -16,7 +16,7 @@ test "default locale is `en`" do
|
||||||
|> SetLocalePlug.call([])
|
|> SetLocalePlug.call([])
|
||||||
|
|
||||||
assert "en" == Gettext.get_locale()
|
assert "en" == Gettext.get_locale()
|
||||||
assert %{locale: "en"} == conn.assigns
|
assert %{locale: "en"} = conn.assigns
|
||||||
end
|
end
|
||||||
|
|
||||||
test "use supported locale from `accept-language`" do
|
test "use supported locale from `accept-language`" do
|
||||||
|
@ -30,7 +30,7 @@ test "use supported locale from `accept-language`" do
|
||||||
|> SetLocalePlug.call([])
|
|> SetLocalePlug.call([])
|
||||||
|
|
||||||
assert "ru" == Gettext.get_locale()
|
assert "ru" == Gettext.get_locale()
|
||||||
assert %{locale: "ru"} == conn.assigns
|
assert %{locale: "ru"} = conn.assigns
|
||||||
end
|
end
|
||||||
|
|
||||||
test "fallback to the general language if a variant is not supported" do
|
test "fallback to the general language if a variant is not supported" do
|
||||||
|
@ -44,7 +44,7 @@ test "fallback to the general language if a variant is not supported" do
|
||||||
|> SetLocalePlug.call([])
|
|> SetLocalePlug.call([])
|
||||||
|
|
||||||
assert "ru" == Gettext.get_locale()
|
assert "ru" == Gettext.get_locale()
|
||||||
assert %{locale: "ru"} == conn.assigns
|
assert %{locale: "ru"} = conn.assigns
|
||||||
end
|
end
|
||||||
|
|
||||||
test "use supported locale with specifiers from `accept-language`" do
|
test "use supported locale with specifiers from `accept-language`" do
|
||||||
|
@ -58,7 +58,21 @@ test "use supported locale with specifiers from `accept-language`" do
|
||||||
|> SetLocalePlug.call([])
|
|> SetLocalePlug.call([])
|
||||||
|
|
||||||
assert "zh_Hans" == Gettext.get_locale()
|
assert "zh_Hans" == Gettext.get_locale()
|
||||||
assert %{locale: "zh_Hans"} == conn.assigns
|
assert %{locale: "zh_Hans"} = conn.assigns
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it assigns all supported locales" do
|
||||||
|
conn =
|
||||||
|
:get
|
||||||
|
|> conn("/cofe")
|
||||||
|
|> Conn.put_req_header(
|
||||||
|
"accept-language",
|
||||||
|
"ru, fr-CH, fr;q=0.9, en;q=0.8, x-unsupported;q=0.8, *;q=0.5"
|
||||||
|
)
|
||||||
|
|> SetLocalePlug.call([])
|
||||||
|
|
||||||
|
assert "ru" == Gettext.get_locale()
|
||||||
|
assert %{locale: "ru", locales: ["ru", "fr", "en"]} = conn.assigns
|
||||||
end
|
end
|
||||||
|
|
||||||
test "fallback to some variant of the language if the unqualified language is not supported" do
|
test "fallback to some variant of the language if the unqualified language is not supported" do
|
||||||
|
@ -87,7 +101,7 @@ test "use supported locale from cookie" do
|
||||||
|> SetLocalePlug.call([])
|
|> SetLocalePlug.call([])
|
||||||
|
|
||||||
assert "zh_Hans" == Gettext.get_locale()
|
assert "zh_Hans" == Gettext.get_locale()
|
||||||
assert %{locale: "zh_Hans"} == conn.assigns
|
assert %{locale: "zh_Hans"} = conn.assigns
|
||||||
end
|
end
|
||||||
|
|
||||||
test "fallback to supported locale from `accept-language` if locale in cookie not supported" do
|
test "fallback to supported locale from `accept-language` if locale in cookie not supported" do
|
||||||
|
@ -102,7 +116,7 @@ test "fallback to supported locale from `accept-language` if locale in cookie no
|
||||||
|> SetLocalePlug.call([])
|
|> SetLocalePlug.call([])
|
||||||
|
|
||||||
assert "ru" == Gettext.get_locale()
|
assert "ru" == Gettext.get_locale()
|
||||||
assert %{locale: "ru"} == conn.assigns
|
assert %{locale: "ru"} = conn.assigns
|
||||||
end
|
end
|
||||||
|
|
||||||
test "fallback to default if nothing is supported" do
|
test "fallback to default if nothing is supported" do
|
||||||
|
@ -117,7 +131,7 @@ test "fallback to default if nothing is supported" do
|
||||||
|> SetLocalePlug.call([])
|
|> SetLocalePlug.call([])
|
||||||
|
|
||||||
assert "en" == Gettext.get_locale()
|
assert "en" == Gettext.get_locale()
|
||||||
assert %{locale: "en"} == conn.assigns
|
assert %{locale: "en"} = conn.assigns
|
||||||
end
|
end
|
||||||
|
|
||||||
test "use default locale if locale from `accept-language` is not supported" do
|
test "use default locale if locale from `accept-language` is not supported" do
|
||||||
|
@ -128,6 +142,6 @@ test "use default locale if locale from `accept-language` is not supported" do
|
||||||
|> SetLocalePlug.call([])
|
|> SetLocalePlug.call([])
|
||||||
|
|
||||||
assert "en" == Gettext.get_locale()
|
assert "en" == Gettext.get_locale()
|
||||||
assert %{locale: "en"} == conn.assigns
|
assert %{locale: "en"} = conn.assigns
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue