Support multiple locales formally
authorTusooa Zhu <tusooa@kazv.moe>
Thu, 3 Mar 2022 07:03:44 +0000 (02:03 -0500)
committerFloatingGhost <hannah@coffee-and-dreams.uk>
Wed, 29 Jun 2022 19:47:10 +0000 (20:47 +0100)
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

lib/pleroma/web/gettext.ex
lib/pleroma/web/plugs/set_locale_plug.ex
test/pleroma/web/plugs/set_locale_plug_test.exs

index 694ad8ad67e9e7043114c6a2e8a0b2b25e3ed0ff..e17451c09b53b8a441510a694411d381827e3f48 100644 (file)
@@ -65,6 +65,24 @@ defmodule Pleroma.Web.Gettext 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 @@ defmodule Pleroma.Web.Gettext 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
index 78ae566c778bcd7ab63ae4f4f97374c91c357f9b..936f65f5da346def07fc844bcdfbbacd0060a360 100644 (file)
@@ -11,22 +11,27 @@ defmodule Pleroma.Web.Plugs.SetLocalePlug do
   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
index f5d3ab995d69def1fefde6ab57b394c8f8a47a8f..b0e7afffdc1fbc54cd0a2727c6e71a6775b9cb0c 100644 (file)
@@ -16,7 +16,7 @@ defmodule Pleroma.Web.Plugs.SetLocalePlugTest 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 @@ defmodule Pleroma.Web.Plugs.SetLocalePlugTest 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 @@ defmodule Pleroma.Web.Plugs.SetLocalePlugTest 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 @@ defmodule Pleroma.Web.Plugs.SetLocalePlugTest 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 @@ defmodule Pleroma.Web.Plugs.SetLocalePlugTest 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 @@ defmodule Pleroma.Web.Plugs.SetLocalePlugTest do
       |> 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 @@ defmodule Pleroma.Web.Plugs.SetLocalePlugTest 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 @@ defmodule Pleroma.Web.Plugs.SetLocalePlugTest do
       |> SetLocalePlug.call([])
 
     assert "en" == Gettext.get_locale()
-    assert %{locale: "en"} == conn.assigns
+    assert %{locale: "en"} = conn.assigns
   end
 end