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
parent d3f3f30c6a
commit 7ea330b4fe
No known key found for this signature in database
GPG Key ID: 7B467EDE43A08224
3 changed files with 89 additions and 17 deletions

View File

@ -65,6 +65,24 @@ def supported_variants_of_locale(locale) do
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
if supports_locale?(locale) do
locale
@ -73,11 +91,46 @@ def locale_or_default(locale) do
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
Gettext.with_locale(Pleroma.Web.Gettext.locale_or_default(unquote(locale)), fn ->
Pleroma.Web.Gettext.with_locales_func(unquote(locales), fn ->
unquote(fun)
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

View File

@ -11,22 +11,27 @@ def frontend_language_cookie_name, do: "userLanguage"
def init(_), do: nil
def call(conn, _) do
locale = get_locale_from_header(conn) || Gettext.get_locale()
Gettext.put_locale(locale)
assign(conn, :locale, locale)
locales = get_locales_from_header(conn)
first_locale = Enum.at(locales, 0, Gettext.get_locale())
Pleroma.Web.Gettext.put_locales(locales)
conn
|> assign(:locale, first_locale)
|> assign(:locales, locales)
end
defp get_locale_from_header(conn) do
defp get_locales_from_header(conn) do
conn
|> extract_preferred_language()
|> normalize_language_codes()
|> first_supported()
|> all_supported()
end
defp first_supported(locales) do
defp all_supported(locales) do
locales
|> Enum.flat_map(&Pleroma.Web.Gettext.supported_variants_of_locale/1)
|> Enum.find(&supported_locale?/1)
|> Enum.filter(&supported_locale?/1)
end
defp normalize_language_codes(codes) do

View File

@ -16,7 +16,7 @@ test "default locale is `en`" do
|> SetLocalePlug.call([])
assert "en" == Gettext.get_locale()
assert %{locale: "en"} == conn.assigns
assert %{locale: "en"} = conn.assigns
end
test "use supported locale from `accept-language`" do
@ -30,7 +30,7 @@ test "use supported locale from `accept-language`" do
|> SetLocalePlug.call([])
assert "ru" == Gettext.get_locale()
assert %{locale: "ru"} == conn.assigns
assert %{locale: "ru"} = conn.assigns
end
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([])
assert "ru" == Gettext.get_locale()
assert %{locale: "ru"} == conn.assigns
assert %{locale: "ru"} = conn.assigns
end
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([])
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
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([])
assert "zh_Hans" == Gettext.get_locale()
assert %{locale: "zh_Hans"} == conn.assigns
assert %{locale: "zh_Hans"} = conn.assigns
end
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([])
assert "ru" == Gettext.get_locale()
assert %{locale: "ru"} == conn.assigns
assert %{locale: "ru"} = conn.assigns
end
test "fallback to default if nothing is supported" do
@ -117,7 +131,7 @@ test "fallback to default if nothing is supported" do
|> SetLocalePlug.call([])
assert "en" == Gettext.get_locale()
assert %{locale: "en"} == conn.assigns
assert %{locale: "en"} = conn.assigns
end
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([])
assert "en" == Gettext.get_locale()
assert %{locale: "en"} == conn.assigns
assert %{locale: "en"} = conn.assigns
end
end