logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://hacktivis.me/git/pleroma.git

gettext.ex (5618B)


  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.Gettext do
  5. @moduledoc """
  6. A module providing Internationalization with a gettext-based API.
  7. By using [Gettext](https://hexdocs.pm/gettext),
  8. your module gains a set of macros for translations, for example:
  9. import Pleroma.Web.Gettext
  10. # Simple translation
  11. gettext "Here is the string to translate"
  12. # Plural translation
  13. ngettext "Here is the string to translate",
  14. "Here are the strings to translate",
  15. 3
  16. # Domain-based translation
  17. dgettext "errors", "Here is the error message to translate"
  18. See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage.
  19. """
  20. use Gettext, otp_app: :pleroma
  21. def language_tag do
  22. # Naive implementation: HTML lang attribute uses BCP 47, which
  23. # uses - as a separator.
  24. # https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang
  25. Gettext.get_locale()
  26. |> String.replace("_", "-", global: true)
  27. end
  28. def normalize_locale(locale) do
  29. if is_binary(locale) do
  30. String.replace(locale, "-", "_", global: true)
  31. else
  32. nil
  33. end
  34. end
  35. def supports_locale?(locale) do
  36. Pleroma.Web.Gettext
  37. |> Gettext.known_locales()
  38. |> Enum.member?(locale)
  39. end
  40. def variant?(locale), do: String.contains?(locale, "_")
  41. def language_for_variant(locale) do
  42. Enum.at(String.split(locale, "_"), 0)
  43. end
  44. def ensure_fallbacks(locales) do
  45. locales
  46. |> Enum.flat_map(fn locale ->
  47. others =
  48. other_supported_variants_of_locale(locale)
  49. |> Enum.filter(fn l -> not Enum.member?(locales, l) end)
  50. [locale] ++ others
  51. end)
  52. end
  53. def other_supported_variants_of_locale(locale) do
  54. cond do
  55. supports_locale?(locale) ->
  56. []
  57. variant?(locale) ->
  58. lang = language_for_variant(locale)
  59. if supports_locale?(lang), do: [lang], else: []
  60. true ->
  61. Gettext.known_locales(Pleroma.Web.Gettext)
  62. |> Enum.filter(fn l -> String.starts_with?(l, locale <> "_") end)
  63. end
  64. end
  65. def get_locales do
  66. Process.get({Pleroma.Web.Gettext, :locales}, [])
  67. end
  68. def locale_list?(locales) do
  69. Enum.all?(locales, &is_binary/1)
  70. end
  71. def put_locales(locales) do
  72. if locale_list?(locales) do
  73. Process.put({Pleroma.Web.Gettext, :locales}, Enum.uniq(locales))
  74. Gettext.put_locale(Enum.at(locales, 0, Gettext.get_locale()))
  75. :ok
  76. else
  77. {:error, :not_locale_list}
  78. end
  79. end
  80. def locale_or_default(locale) do
  81. if supports_locale?(locale) do
  82. locale
  83. else
  84. Gettext.get_locale()
  85. end
  86. end
  87. def with_locales_func(locales, fun) do
  88. prev_locales = Process.get({Pleroma.Web.Gettext, :locales})
  89. put_locales(locales)
  90. try do
  91. fun.()
  92. after
  93. if prev_locales do
  94. put_locales(prev_locales)
  95. else
  96. Process.delete({Pleroma.Web.Gettext, :locales})
  97. Process.delete(Gettext)
  98. end
  99. end
  100. end
  101. defmacro with_locales(locales, do: fun) do
  102. quote do
  103. Pleroma.Web.Gettext.with_locales_func(unquote(locales), fn ->
  104. unquote(fun)
  105. end)
  106. end
  107. end
  108. def to_locale_list(locale) when is_binary(locale) do
  109. locale
  110. |> String.split(",")
  111. |> Enum.filter(&supports_locale?/1)
  112. end
  113. def to_locale_list(_), do: []
  114. defmacro with_locale_or_default(locale, do: fun) do
  115. quote do
  116. Pleroma.Web.Gettext.with_locales_func(
  117. Pleroma.Web.Gettext.to_locale_list(unquote(locale))
  118. |> Enum.concat(Pleroma.Web.Gettext.get_locales()),
  119. fn ->
  120. unquote(fun)
  121. end
  122. )
  123. end
  124. end
  125. defp next_locale(locale, list) do
  126. index = Enum.find_index(list, fn item -> item == locale end)
  127. if not is_nil(index) do
  128. Enum.at(list, index + 1)
  129. else
  130. nil
  131. end
  132. end
  133. # We do not yet have a proper English translation. The "English"
  134. # version is currently but the fallback msgid. However, this
  135. # will not work if the user puts English as the first language,
  136. # and at the same time specifies other languages, as gettext will
  137. # think the English translation is missing, and call
  138. # handle_missing_translation functions. This may result in
  139. # text in other languages being shown even if English is preferred
  140. # by the user.
  141. #
  142. # To prevent this, we do not allow fallbacking when the current
  143. # locale missing a translation is English.
  144. defp should_fallback?(locale) do
  145. locale != "en"
  146. end
  147. def handle_missing_translation(locale, domain, msgctxt, msgid, bindings) do
  148. next = next_locale(locale, get_locales())
  149. if is_nil(next) or not should_fallback?(locale) do
  150. super(locale, domain, msgctxt, msgid, bindings)
  151. else
  152. {:ok,
  153. Gettext.with_locale(next, fn ->
  154. Gettext.dpgettext(Pleroma.Web.Gettext, domain, msgctxt, msgid, bindings)
  155. end)}
  156. end
  157. end
  158. def handle_missing_plural_translation(
  159. locale,
  160. domain,
  161. msgctxt,
  162. msgid,
  163. msgid_plural,
  164. n,
  165. bindings
  166. ) do
  167. next = next_locale(locale, get_locales())
  168. if is_nil(next) or not should_fallback?(locale) do
  169. super(locale, domain, msgctxt, msgid, msgid_plural, n, bindings)
  170. else
  171. {:ok,
  172. Gettext.with_locale(next, fn ->
  173. Gettext.dpngettext(
  174. Pleroma.Web.Gettext,
  175. domain,
  176. msgctxt,
  177. msgid,
  178. msgid_plural,
  179. n,
  180. bindings
  181. )
  182. end)}
  183. end
  184. end
  185. end