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:
Tusooa Zhu 2022-03-03 02:03:44 -05:00 committed by FloatingGhost
parent 845e5769ce
commit fa95bc8725
3 changed files with 89 additions and 17 deletions

View file

@ -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

View file

@ -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

View file

@ -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