3c3fffa81a9ff33ff4da5c046ea64951e3d6f921
[akkoma] / lib / pleroma / web / plugs / set_locale_plug.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 # NOTE: this module is based on https://github.com/smeevil/set_locale
6 defmodule Pleroma.Web.Plugs.SetLocalePlug do
7 import Plug.Conn, only: [get_req_header: 2, assign: 3]
8
9 def frontend_language_cookie_name, do: "userLanguage"
10
11 def init(_), do: nil
12
13 def call(conn, _) do
14 locale = get_locale_from_header(conn) || Gettext.get_locale()
15 Gettext.put_locale(locale)
16 assign(conn, :locale, locale)
17 end
18
19 defp get_locale_from_header(conn) do
20 conn
21 |> extract_preferred_language()
22 |> normalize_language_codes()
23 |> Enum.find(&supported_locale?/1)
24 end
25
26 defp normalize_language_codes(codes) do
27 codes
28 |> Enum.map(fn code -> String.replace(code, "-", "_") end)
29 end
30
31 defp extract_preferred_language(conn) do
32 extract_frontend_language(conn) ++ extract_accept_language(conn)
33 end
34
35 defp extract_frontend_language(conn) do
36 %{req_cookies: cookies} =
37 conn
38 |> Plug.Conn.fetch_cookies()
39
40 case cookies[frontend_language_cookie_name()] do
41 nil ->
42 []
43
44 fe_lang ->
45 [fe_lang]
46 |> ensure_language_fallbacks()
47 end
48 end
49
50 defp extract_accept_language(conn) do
51 case get_req_header(conn, "accept-language") do
52 [value | _] ->
53 value
54 |> String.split(",")
55 |> Enum.map(&parse_language_option/1)
56 |> Enum.sort(&(&1.quality > &2.quality))
57 |> Enum.map(& &1.tag)
58 |> Enum.reject(&is_nil/1)
59 |> ensure_language_fallbacks()
60
61 _ ->
62 []
63 end
64 end
65
66 defp supported_locale?(locale) do
67 Pleroma.Web.Gettext.supports_locale?(locale)
68 end
69
70 defp parse_language_option(string) do
71 captures = Regex.named_captures(~r/^\s?(?<tag>[\w\-]+)(?:;q=(?<quality>[\d\.]+))?$/i, string)
72
73 quality =
74 case Float.parse(captures["quality"] || "1.0") do
75 {val, _} -> val
76 :error -> 1.0
77 end
78
79 %{tag: captures["tag"], quality: quality}
80 end
81
82 defp ensure_language_fallbacks(tags) do
83 Enum.flat_map(tags, fn tag ->
84 [language | _] = String.split(tag, "-")
85 if Enum.member?(tags, language), do: [tag], else: [tag, language]
86 end)
87 end
88 end