Add SetLocalePlug
authorEgor Kislitsyn <egor@kislitsyn.com>
Tue, 9 Jul 2019 11:30:15 +0000 (18:30 +0700)
committerEgor Kislitsyn <egor@kislitsyn.com>
Wed, 10 Jul 2019 11:08:03 +0000 (18:08 +0700)
lib/pleroma/plugs/set_locale_plug.ex [new file with mode: 0644]
lib/pleroma/web/endpoint.ex
test/plugs/set_locale_plug_test.exs [new file with mode: 0644]

diff --git a/lib/pleroma/plugs/set_locale_plug.ex b/lib/pleroma/plugs/set_locale_plug.ex
new file mode 100644 (file)
index 0000000..8646cb3
--- /dev/null
@@ -0,0 +1,63 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+# NOTE: this module is based on https://github.com/smeevil/set_locale
+defmodule Pleroma.Plugs.SetLocalePlug do
+  import Plug.Conn, only: [get_req_header: 2, assign: 3]
+
+  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)
+  end
+
+  defp get_locale_from_header(conn) do
+    conn
+    |> extract_accept_language()
+    |> Enum.find(&supported_locale?/1)
+  end
+
+  defp extract_accept_language(conn) do
+    case get_req_header(conn, "accept-language") do
+      [value | _] ->
+        value
+        |> String.split(",")
+        |> Enum.map(&parse_language_option/1)
+        |> Enum.sort(&(&1.quality > &2.quality))
+        |> Enum.map(& &1.tag)
+        |> Enum.reject(&is_nil/1)
+        |> ensure_language_fallbacks()
+
+      _ ->
+        []
+    end
+  end
+
+  defp supported_locale?(locale) do
+    Pleroma.Web.Gettext
+    |> Gettext.known_locales()
+    |> Enum.member?(locale)
+  end
+
+  defp parse_language_option(string) do
+    captures = Regex.named_captures(~r/^\s?(?<tag>[\w\-]+)(?:;q=(?<quality>[\d\.]+))?$/i, string)
+
+    quality =
+      case Float.parse(captures["quality"] || "1.0") do
+        {val, _} -> val
+        :error -> 1.0
+      end
+
+    %{tag: captures["tag"], quality: quality}
+  end
+
+  defp ensure_language_fallbacks(tags) do
+    Enum.flat_map(tags, fn tag ->
+      [language | _] = String.split(tag, "-")
+      if Enum.member?(tags, language), do: [tag], else: [tag, language]
+    end)
+  end
+end
index ddaf88f1d8630e6eb3fd4afd0aa941931cb90249..c123530dcf961c10a2dfd5179404c3a43c53613f 100644 (file)
@@ -7,13 +7,9 @@ defmodule Pleroma.Web.Endpoint do
 
   socket("/socket", Pleroma.Web.UserSocket)
 
-  # Serve at "/" the static files from "priv/static" directory.
-  #
-  # You should set gzip to true if you are running phoenix.digest
-  # when deploying your static files in production.
+  plug(Pleroma.Plugs.SetLocalePlug)
   plug(CORSPlug)
   plug(Pleroma.Plugs.HTTPSecurityPlug)
-
   plug(Pleroma.Plugs.UploadedMedia)
 
   @static_cache_control "public, no-cache"
@@ -30,6 +26,10 @@ defmodule Pleroma.Web.Endpoint do
     }
   )
 
+  # Serve at "/" the static files from "priv/static" directory.
+  #
+  # You should set gzip to true if you are running phoenix.digest
+  # when deploying your static files in production.
   plug(
     Plug.Static,
     at: "/",
diff --git a/test/plugs/set_locale_plug_test.exs b/test/plugs/set_locale_plug_test.exs
new file mode 100644 (file)
index 0000000..3e31b0a
--- /dev/null
@@ -0,0 +1,46 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Plugs.SetLocalePlugTest do
+  use ExUnit.Case, async: true
+  use Plug.Test
+
+  alias Plug.Conn
+  alias Pleroma.Plugs.SetLocalePlug
+
+  test "default locale is `en`" do
+    conn =
+      :get
+      |> conn("/cofe")
+      |> SetLocalePlug.call([])
+
+    assert "en" == Gettext.get_locale()
+    assert %{locale: "en"} == conn.assigns
+  end
+
+  test "use supported locale from `accept-language`" do
+    conn =
+      :get
+      |> conn("/cofe")
+      |> Conn.put_req_header(
+        "accept-language",
+        "ru, fr-CH, fr;q=0.9, en;q=0.8, *;q=0.5"
+      )
+      |> SetLocalePlug.call([])
+
+    assert "ru" == Gettext.get_locale()
+    assert %{locale: "ru"} == conn.assigns
+  end
+
+  test "use default locale if locale from `accept-language` is not supported" do
+    conn =
+      :get
+      |> conn("/cofe")
+      |> Conn.put_req_header("accept-language", "tlh")
+      |> SetLocalePlug.call([])
+
+    assert "en" == Gettext.get_locale()
+    assert %{locale: "en"} == conn.assigns
+  end
+end