From 0fd3695b9c884cbc05f07c45249eb0e291cf6d1d Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Mon, 21 Feb 2022 17:54:18 -0500 Subject: [PATCH 01/37] Prefer userLanguage cookie over Accept-Language header in detecting locale https://git.pleroma.social/pleroma/pleroma-meta/-/issues/60 --- lib/pleroma/web/plugs/set_locale_plug.ex | 29 ++++++++- .../web/plugs/set_locale_plug_test.exs | 59 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/plugs/set_locale_plug.ex b/lib/pleroma/web/plugs/set_locale_plug.ex index d77191cffb..446baf24b8 100644 --- a/lib/pleroma/web/plugs/set_locale_plug.ex +++ b/lib/pleroma/web/plugs/set_locale_plug.ex @@ -6,6 +6,8 @@ defmodule Pleroma.Web.Plugs.SetLocalePlug do import Plug.Conn, only: [get_req_header: 2, assign: 3] + def frontend_language_cookie_name(), do: "userLanguage" + def init(_), do: nil def call(conn, _) do @@ -16,10 +18,35 @@ def call(conn, _) do defp get_locale_from_header(conn) do conn - |> extract_accept_language() + |> extract_preferred_language() + |> normalize_language_codes() |> Enum.find(&supported_locale?/1) end + defp normalize_language_codes(codes) do + codes + |> Enum.map(fn code -> String.replace(code, "-", "_") end) + end + + defp extract_preferred_language(conn) do + extract_frontend_language(conn) ++ extract_accept_language(conn) + end + + defp extract_frontend_language(conn) do + %{req_cookies: cookies} = + conn + |> Plug.Conn.fetch_cookies() + + case cookies[frontend_language_cookie_name()] do + nil -> + [] + + fe_lang -> + [fe_lang] + |> ensure_language_fallbacks() + end + end + defp extract_accept_language(conn) do case get_req_header(conn, "accept-language") do [value | _] -> diff --git a/test/pleroma/web/plugs/set_locale_plug_test.exs b/test/pleroma/web/plugs/set_locale_plug_test.exs index 5261e67aec..043d7eb182 100644 --- a/test/pleroma/web/plugs/set_locale_plug_test.exs +++ b/test/pleroma/web/plugs/set_locale_plug_test.exs @@ -33,6 +33,65 @@ test "use supported locale from `accept-language`" do assert %{locale: "ru"} == conn.assigns end + test "use supported locale with specifiers from `accept-language`" do + conn = + :get + |> conn("/cofe") + |> Conn.put_req_header( + "accept-language", + "zh-Hans;q=0.9, en;q=0.8, *;q=0.5" + ) + |> SetLocalePlug.call([]) + + assert "zh_Hans" == Gettext.get_locale() + assert %{locale: "zh_Hans"} == conn.assigns + end + + test "use supported locale from cookie" do + conn = + :get + |> conn("/cofe") + |> put_req_cookie(SetLocalePlug.frontend_language_cookie_name(), "zh-Hans") + |> Conn.put_req_header( + "accept-language", + "ru, fr-CH, fr;q=0.9, en;q=0.8, *;q=0.5" + ) + |> SetLocalePlug.call([]) + + assert "zh_Hans" == Gettext.get_locale() + assert %{locale: "zh_Hans"} == conn.assigns + end + + test "fallback to supported locale from `accept-language` if locale in cookie not supported" do + conn = + :get + |> conn("/cofe") + |> put_req_cookie(SetLocalePlug.frontend_language_cookie_name(), "x-nonexist") + |> 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 "fallback to default if nothing is supported" do + conn = + :get + |> conn("/cofe") + |> put_req_cookie(SetLocalePlug.frontend_language_cookie_name(), "x-nonexist") + |> Conn.put_req_header( + "accept-language", + "x-nonexist" + ) + |> SetLocalePlug.call([]) + + assert "en" == Gettext.get_locale() + assert %{locale: "en"} == conn.assigns + end + test "use default locale if locale from `accept-language` is not supported" do conn = :get From a8671074375842463ca2f848855db3414cd49105 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Mon, 21 Feb 2022 18:42:25 -0500 Subject: [PATCH 02/37] Make remote follow pages translatable --- .../templates/twitter_api/remote_follow/follow.html.eex | 6 +++--- .../twitter_api/remote_follow/follow_login.html.eex | 8 ++++---- .../twitter_api/remote_follow/follow_mfa.html.eex | 6 +++--- .../templates/twitter_api/remote_follow/followed.html.eex | 5 ++--- .../web/templates/twitter_api/util/subscribe.html.eex | 8 ++++---- lib/pleroma/web/twitter_api/views/remote_follow_view.ex | 1 + lib/pleroma/web/twitter_api/views/util_view.ex | 1 + 7 files changed, 18 insertions(+), 17 deletions(-) diff --git a/lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex b/lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex index a7be530914..e2d251faca 100644 --- a/lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex +++ b/lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex @@ -1,11 +1,11 @@ <%= if @error == :error do %> -

Error fetching user

+

<%= Gettext.dpgettext("static_pages", "remote follow error", "Error fetching user") %>

<% else %> -

Remote follow

+

<%= Gettext.dpgettext("static_pages", "remote follow header", "Remote follow") %>

<%= @followee.nickname %>

<%= form_for @conn, Routes.remote_follow_path(@conn, :do_follow), [as: "user"], fn f -> %> <%= hidden_input f, :id, value: @followee.id %> - <%= submit "Authorize" %> + <%= submit Gettext.dpgettext("static_pages", "remote follow authorization button", "Authorize") %> <% end %> <% end %> diff --git a/lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex b/lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex index bc5fb28e3c..26340a906f 100644 --- a/lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex +++ b/lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex @@ -1,14 +1,14 @@ <%= if @error do %>

<%= @error %>

<% end %> -

Log in to follow

+

<%= Gettext.dpgettext("static_pages", "remote follow header, need login", "Log in to follow") %>

<%= @followee.nickname %>

<%= form_for @conn, Routes.remote_follow_path(@conn, :do_follow), [as: "authorization"], fn f -> %> -<%= text_input f, :name, placeholder: "Username", required: true, autocomplete: "username" %> +<%= text_input f, :name, placeholder: Gettext.dpgettext("static_pages", "placeholder text for username entry", "Username"), required: true, autocomplete: "username" %>
-<%= password_input f, :password, placeholder: "Password", required: true, autocomplete: "password" %> +<%= password_input f, :password, placeholder: Gettext.dpgettext("static_pages", "placeholder text for password entry", "Password"), required: true, autocomplete: "password" %>
<%= hidden_input f, :id, value: @followee.id %> -<%= submit "Authorize" %> +<%= submit Gettext.dpgettext("static_pages", "remote follow authorization button for login", "Authorize") %> <% end %> diff --git a/lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex b/lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex index a54ed83b59..638212c1e3 100644 --- a/lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex +++ b/lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex @@ -1,13 +1,13 @@ <%= if @error do %>

<%= @error %>

<% end %> -

Two-factor authentication

+

<%= Gettext.dpgettext("static_pages", "remote follow mfa header", "Two-factor authentication") %>

<%= @followee.nickname %>

<%= form_for @conn, Routes.remote_follow_path(@conn, :do_follow), [as: "mfa"], fn f -> %> -<%= text_input f, :code, placeholder: "Authentication code", required: true %> +<%= text_input f, :code, placeholder: Gettext.dpgettext("static_pages", "placeholder text for auth code entry", "Authentication code"), required: true %>
<%= hidden_input f, :id, value: @followee.id %> <%= hidden_input f, :token, value: @mfa_token %> -<%= submit "Authorize" %> +<%= submit Gettext.dpgettext("static_pages", "remote follow authorization button for mfa", "Authorize") %> <% end %> diff --git a/lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex b/lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex index da473d502c..2fb4cc5d36 100644 --- a/lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex +++ b/lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex @@ -1,6 +1,5 @@ <%= if @error do %> -

Error following account

+

<%= Gettext.dpgettext("static_pages", "remote follow error", "Error following account") %>

<% else %> -

Account followed!

+

<%= Gettext.dpgettext("static_pages", "remote follow success", "Account followed!") %>

<% end %> - diff --git a/lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex b/lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex index a6b313d8ae..848660f264 100644 --- a/lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex +++ b/lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex @@ -1,10 +1,10 @@ <%= if @error do %> -

Error: <%= @error %>

+

<%= Gettext.dpgettext("static_pages", "remote follow error", "Error: %{error}", error: @error) %>

<% else %> -

Remotely follow <%= @nickname %>

+

<%= Gettext.dpgettext("static_pages", "remote follow header", "Remotely follow %{nickname}", nickname: @nickname) %>

<%= form_for @conn, Routes.util_path(@conn, :remote_subscribe), [as: "user"], fn f -> %> <%= hidden_input f, :nickname, value: @nickname %> - <%= text_input f, :profile, placeholder: "Your account ID, e.g. lain@quitter.se" %> - <%= submit "Follow" %> + <%= text_input f, :profile, placeholder: Gettext.dpgettext("static_pages", "placeholder text for account id", "Your account ID, e.g. lain@quitter.se") %> + <%= submit Gettext.dpgettext("static_pages", "remote follow authorization button for following with a remote account", "Follow") %> <% end %> <% end %> diff --git a/lib/pleroma/web/twitter_api/views/remote_follow_view.ex b/lib/pleroma/web/twitter_api/views/remote_follow_view.ex index ac3f15eec3..618ba2ba58 100644 --- a/lib/pleroma/web/twitter_api/views/remote_follow_view.ex +++ b/lib/pleroma/web/twitter_api/views/remote_follow_view.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowView do use Pleroma.Web, :view import Phoenix.HTML.Form + alias Pleroma.Web.Gettext defdelegate avatar_url(user), to: Pleroma.User end diff --git a/lib/pleroma/web/twitter_api/views/util_view.ex b/lib/pleroma/web/twitter_api/views/util_view.ex index 87cb79dd79..a03020290b 100644 --- a/lib/pleroma/web/twitter_api/views/util_view.ex +++ b/lib/pleroma/web/twitter_api/views/util_view.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilView do import Phoenix.HTML.Form alias Pleroma.Config alias Pleroma.Web.Endpoint + alias Pleroma.Web.Gettext def status_net_config(instance) do """ From 2fa1ca84e62969d7f014f5cb9add530fcb624bca Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Mon, 21 Feb 2022 18:44:36 -0500 Subject: [PATCH 03/37] Extract translatable text --- priv/gettext/default.pot | 185 ++++++++++++++++++++ priv/gettext/errors.pot | 315 +++++++++++++++++----------------- priv/gettext/posix_errors.pot | 88 +++++----- priv/gettext/static_pages.pot | 107 ++++++++++++ 4 files changed, 490 insertions(+), 205 deletions(-) create mode 100644 priv/gettext/default.pot create mode 100644 priv/gettext/static_pages.pot diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot new file mode 100644 index 0000000000..fed111ccb0 --- /dev/null +++ b/priv/gettext/default.pot @@ -0,0 +1,185 @@ +## This file is a PO Template file. +## +## "msgid"s here are often extracted from source code. +## Add new translations manually only if they're dynamic +## translations that can't be statically extracted. +## +## Run "mix gettext.extract" to bring this file up to +## date. Leave "msgstr"s empty as changing them here as no +## effect: edit them in PO (.po) files instead. +msgid "" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:122 +msgid "%{name} - %{count} is not a multiple of %{multiple}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:131 +msgid "%{name} - %{value} is larger than exclusive maximum %{max}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:140 +msgid "%{name} - %{value} is larger than inclusive maximum %{max}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:149 +msgid "%{name} - %{value} is smaller than exclusive minimum %{min}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:158 +msgid "%{name} - %{value} is smaller than inclusive minimum %{min}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:102 +msgid "%{name} - Array items must be unique." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:114 +msgid "%{name} - Array length %{length} is larger than maxItems: %{}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:106 +msgid "%{name} - Array length %{length} is smaller than minItems: %{min}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:166 +msgid "%{name} - Invalid %{type}. Got: %{value}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:174 +msgid "%{name} - Invalid format. Expected %{format}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:51 +msgid "%{name} - Invalid schema.type. Got: %{type}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:178 +msgid "%{name} - Invalid value for enum." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:95 +msgid "%{name} - String length is larger than maxLength: %{length}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:88 +msgid "%{name} - String length is smaller than minLength: %{length}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:63 +msgid "%{name} - null value where %{type} expected." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:60 +msgid "%{name} - null value." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:182 +msgid "Failed to cast to any schema in %{polymorphic_type}" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:71 +msgid "Failed to cast value as %{invalid_schema}. Value must be castable using `allOf` schemas listed." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:84 +msgid "Failed to cast value to one of: %{failed_schemas}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:78 +msgid "Failed to cast value using any of: %{failed_schemas}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:212 +msgid "Invalid value for header: %{name}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:204 +msgid "Missing field: %{name}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:208 +msgid "Missing header: %{name}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:196 +msgid "No value provided for required discriminator `%{field}`." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:216 +msgid "Object property count %{property_count} is greater than maxProperties: %{max_properties}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:224 +msgid "Object property count %{property_count} is less than minProperties: %{min_properties}" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/static_fe/static_fe/error.html.eex:2 +msgid "Oops" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:188 +msgid "Unexpected field: %{name}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:200 +msgid "Unknown schema: %{name}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:192 +msgid "Value used as discriminator for `%{field}` matches no schemas." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/embed/show.html.eex:43 +#: lib/pleroma/web/templates/static_fe/static_fe/_notice.html.eex:37 +msgid "announces" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/embed/show.html.eex:44 +#: lib/pleroma/web/templates/static_fe/static_fe/_notice.html.eex:38 +msgid "likes" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/embed/show.html.eex:42 +#: lib/pleroma/web/templates/static_fe/static_fe/_notice.html.eex:36 +msgid "replies" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/embed/show.html.eex:27 +#: lib/pleroma/web/templates/static_fe/static_fe/_notice.html.eex:22 +msgid "sensitive media" +msgstr "" diff --git a/priv/gettext/errors.pot b/priv/gettext/errors.pot index e337226a73..7644fc2305 100644 --- a/priv/gettext/errors.pot +++ b/priv/gettext/errors.pot @@ -90,121 +90,99 @@ msgid "must be equal to %{number}" msgstr "" #, elixir-format -#: lib/pleroma/web/common_api/common_api.ex:505 +#: lib/pleroma/web/common_api.ex:523 msgid "Account not found" msgstr "" #, elixir-format -#: lib/pleroma/web/common_api/common_api.ex:339 +#: lib/pleroma/web/common_api.ex:316 msgid "Already voted" msgstr "" #, elixir-format -#: lib/pleroma/web/oauth/oauth_controller.ex:359 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:402 msgid "Bad request" msgstr "" #, elixir-format -#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:426 -msgid "Can't delete object" -msgstr "" - -#, elixir-format -#: lib/pleroma/web/controller_helper.ex:105 -#: lib/pleroma/web/controller_helper.ex:111 +#: lib/pleroma/web/controller_helper.ex:97 +#: lib/pleroma/web/controller_helper.ex:103 msgid "Can't display this activity" msgstr "" #, elixir-format -#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:285 +#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:324 msgid "Can't find user" msgstr "" #, elixir-format -#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:61 +#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:80 msgid "Can't get favorites" msgstr "" #, elixir-format -#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:438 -msgid "Can't like object" -msgstr "" - -#, elixir-format -#: lib/pleroma/web/common_api/utils.ex:563 +#: lib/pleroma/web/common_api/utils.ex:482 msgid "Cannot post an empty status without attachments" msgstr "" #, elixir-format -#: lib/pleroma/web/common_api/utils.ex:511 +#: lib/pleroma/web/common_api/utils.ex:441 msgid "Comment must be up to %{max_size} characters" msgstr "" #, elixir-format -#: lib/pleroma/config/config_db.ex:191 +#: lib/pleroma/config_db.ex:200 msgid "Config with params %{params} not found" msgstr "" #, elixir-format -#: lib/pleroma/web/common_api/common_api.ex:181 -#: lib/pleroma/web/common_api/common_api.ex:185 +#: lib/pleroma/web/common_api.ex:167 lib/pleroma/web/common_api.ex:171 msgid "Could not delete" msgstr "" #, elixir-format -#: lib/pleroma/web/common_api/common_api.ex:231 +#: lib/pleroma/web/common_api.ex:217 msgid "Could not favorite" msgstr "" #, elixir-format -#: lib/pleroma/web/common_api/common_api.ex:453 -msgid "Could not pin" -msgstr "" - -#, elixir-format -#: lib/pleroma/web/common_api/common_api.ex:278 +#: lib/pleroma/web/common_api.ex:254 msgid "Could not unfavorite" msgstr "" #, elixir-format -#: lib/pleroma/web/common_api/common_api.ex:463 -msgid "Could not unpin" -msgstr "" - -#, elixir-format -#: lib/pleroma/web/common_api/common_api.ex:216 +#: lib/pleroma/web/common_api.ex:202 msgid "Could not unrepeat" msgstr "" #, elixir-format -#: lib/pleroma/web/common_api/common_api.ex:512 -#: lib/pleroma/web/common_api/common_api.ex:521 +#: lib/pleroma/web/common_api.ex:530 lib/pleroma/web/common_api.ex:539 msgid "Could not update state" msgstr "" #, elixir-format -#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:207 +#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:205 msgid "Error." msgstr "" #, elixir-format -#: lib/pleroma/web/twitter_api/twitter_api.ex:106 +#: lib/pleroma/web/twitter_api/twitter_api.ex:99 msgid "Invalid CAPTCHA" msgstr "" #, elixir-format -#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:116 -#: lib/pleroma/web/oauth/oauth_controller.ex:568 +#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:144 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:631 msgid "Invalid credentials" msgstr "" #, elixir-format -#: lib/pleroma/plugs/ensure_authenticated_plug.ex:38 +#: lib/pleroma/web/plugs/ensure_authenticated_plug.ex:42 msgid "Invalid credentials." msgstr "" #, elixir-format -#: lib/pleroma/web/common_api/common_api.ex:355 +#: lib/pleroma/web/common_api.ex:337 msgid "Invalid indices" msgstr "" @@ -214,189 +192,184 @@ msgid "Invalid parameters" msgstr "" #, elixir-format -#: lib/pleroma/web/common_api/utils.ex:414 +#: lib/pleroma/web/common_api/utils.ex:349 msgid "Invalid password." msgstr "" #, elixir-format -#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:220 +#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:254 msgid "Invalid request" msgstr "" #, elixir-format -#: lib/pleroma/web/twitter_api/twitter_api.ex:109 +#: lib/pleroma/web/twitter_api/twitter_api.ex:102 msgid "Kocaptcha service unavailable" msgstr "" #, elixir-format -#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:112 +#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:140 msgid "Missing parameters" msgstr "" #, elixir-format -#: lib/pleroma/web/common_api/utils.ex:547 +#: lib/pleroma/web/common_api/utils.ex:477 msgid "No such conversation" msgstr "" #, elixir-format -#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:388 -#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:414 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:456 +#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:171 +#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:197 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:239 msgid "No such permission_group" msgstr "" #, elixir-format -#: lib/pleroma/plugs/uploaded_media.ex:84 -#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:486 lib/pleroma/web/admin_api/controllers/fallback_controller.ex:11 -#: lib/pleroma/web/feed/user_controller.ex:71 lib/pleroma/web/ostatus/ostatus_controller.ex:143 +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:504 +#: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:11 lib/pleroma/web/feed/tag_controller.ex:16 +#: lib/pleroma/web/feed/user_controller.ex:69 lib/pleroma/web/o_status/o_status_controller.ex:132 +#: lib/pleroma/web/plugs/uploaded_media.ex:84 msgid "Not found" msgstr "" #, elixir-format -#: lib/pleroma/web/common_api/common_api.ex:331 +#: lib/pleroma/web/common_api.ex:308 msgid "Poll's author can't vote" msgstr "" #, elixir-format #: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:20 -#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:37 lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:49 -#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:50 lib/pleroma/web/mastodon_api/controllers/status_controller.ex:306 +#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:39 lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:51 +#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:52 lib/pleroma/web/mastodon_api/controllers/status_controller.ex:326 #: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:71 msgid "Record not found" msgstr "" #, elixir-format #: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:35 -#: lib/pleroma/web/feed/user_controller.ex:77 lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:36 -#: lib/pleroma/web/ostatus/ostatus_controller.ex:149 +#: lib/pleroma/web/feed/user_controller.ex:78 lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:42 +#: lib/pleroma/web/o_status/o_status_controller.ex:138 msgid "Something went wrong" msgstr "" #, elixir-format -#: lib/pleroma/web/common_api/activity_draft.ex:107 +#: lib/pleroma/web/common_api/activity_draft.ex:143 msgid "The message visibility must be direct" msgstr "" #, elixir-format -#: lib/pleroma/web/common_api/utils.ex:573 +#: lib/pleroma/web/common_api/utils.ex:492 msgid "The status is over the character limit" msgstr "" #, elixir-format -#: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:31 +#: lib/pleroma/web/plugs/ensure_public_or_authenticated_plug.ex:36 msgid "This resource requires authentication." msgstr "" #, elixir-format -#: lib/pleroma/plugs/rate_limiter/rate_limiter.ex:206 +#: lib/pleroma/web/plugs/rate_limiter.ex:208 msgid "Throttled" msgstr "" #, elixir-format -#: lib/pleroma/web/common_api/common_api.ex:356 +#: lib/pleroma/web/common_api.ex:338 msgid "Too many choices" msgstr "" #, elixir-format -#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:443 -msgid "Unhandled activity type" -msgstr "" - -#, elixir-format -#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:485 +#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:268 msgid "You can't revoke your own admin status." msgstr "" #, elixir-format -#: lib/pleroma/web/oauth/oauth_controller.ex:221 -#: lib/pleroma/web/oauth/oauth_controller.ex:308 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:243 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:333 msgid "Your account is currently disabled" msgstr "" #, elixir-format -#: lib/pleroma/web/oauth/oauth_controller.ex:183 -#: lib/pleroma/web/oauth/oauth_controller.ex:331 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:205 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:356 msgid "Your login is missing a confirmed e-mail address" msgstr "" #, elixir-format -#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:390 +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:392 msgid "can't read inbox of %{nickname} as %{as_nickname}" msgstr "" #, elixir-format -#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:473 +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:491 msgid "can't update outbox of %{nickname} as %{as_nickname}" msgstr "" #, elixir-format -#: lib/pleroma/web/common_api/common_api.ex:471 +#: lib/pleroma/web/common_api.ex:475 msgid "conversation is already muted" msgstr "" #, elixir-format -#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:314 -#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:492 +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:510 msgid "error" msgstr "" #, elixir-format -#: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:32 +#: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:34 msgid "mascots can only be images" msgstr "" #, elixir-format -#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:62 +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:63 msgid "not found" msgstr "" #, elixir-format -#: lib/pleroma/web/oauth/oauth_controller.ex:394 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:437 msgid "Bad OAuth request." msgstr "" #, elixir-format -#: lib/pleroma/web/twitter_api/twitter_api.ex:115 +#: lib/pleroma/web/twitter_api/twitter_api.ex:108 msgid "CAPTCHA already used" msgstr "" #, elixir-format -#: lib/pleroma/web/twitter_api/twitter_api.ex:112 +#: lib/pleroma/web/twitter_api/twitter_api.ex:105 msgid "CAPTCHA expired" msgstr "" #, elixir-format -#: lib/pleroma/plugs/uploaded_media.ex:57 +#: lib/pleroma/web/plugs/uploaded_media.ex:57 msgid "Failed" msgstr "" #, elixir-format -#: lib/pleroma/web/oauth/oauth_controller.ex:410 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:453 msgid "Failed to authenticate: %{message}." msgstr "" #, elixir-format -#: lib/pleroma/web/oauth/oauth_controller.ex:441 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:484 msgid "Failed to set up user account." msgstr "" #, elixir-format -#: lib/pleroma/plugs/oauth_scopes_plug.ex:38 +#: lib/pleroma/web/plugs/o_auth_scopes_plug.ex:37 msgid "Insufficient permissions: %{permissions}." msgstr "" #, elixir-format -#: lib/pleroma/plugs/uploaded_media.ex:104 +#: lib/pleroma/web/plugs/uploaded_media.ex:111 msgid "Internal Error" msgstr "" #, elixir-format -#: lib/pleroma/web/oauth/fallback_controller.ex:22 -#: lib/pleroma/web/oauth/fallback_controller.ex:29 +#: lib/pleroma/web/o_auth/fallback_controller.ex:22 +#: lib/pleroma/web/o_auth/fallback_controller.ex:29 msgid "Invalid Username/Password" msgstr "" #, elixir-format -#: lib/pleroma/web/twitter_api/twitter_api.ex:118 +#: lib/pleroma/web/twitter_api/twitter_api.ex:111 msgid "Invalid answer data" msgstr "" @@ -406,28 +379,28 @@ msgid "Nodeinfo schema version not handled" msgstr "" #, elixir-format -#: lib/pleroma/web/oauth/oauth_controller.ex:172 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:194 msgid "This action is outside the authorized scopes" msgstr "" #, elixir-format -#: lib/pleroma/web/oauth/fallback_controller.ex:14 +#: lib/pleroma/web/o_auth/fallback_controller.ex:14 msgid "Unknown error, please check the details and try again." msgstr "" #, elixir-format -#: lib/pleroma/web/oauth/oauth_controller.ex:119 -#: lib/pleroma/web/oauth/oauth_controller.ex:158 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:136 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:180 msgid "Unlisted redirect_uri." msgstr "" #, elixir-format -#: lib/pleroma/web/oauth/oauth_controller.ex:390 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:433 msgid "Unsupported OAuth provider: %{provider}." msgstr "" #, elixir-format -#: lib/pleroma/uploaders/uploader.ex:72 +#: lib/pleroma/uploaders/uploader.ex:74 msgid "Uploader callback timeout" msgstr "" @@ -437,120 +410,101 @@ msgid "bad request" msgstr "" #, elixir-format -#: lib/pleroma/web/twitter_api/twitter_api.ex:103 +#: lib/pleroma/web/twitter_api/twitter_api.ex:96 msgid "CAPTCHA Error" msgstr "" #, elixir-format -#: lib/pleroma/web/common_api/common_api.ex:290 +#: lib/pleroma/web/common_api.ex:266 msgid "Could not add reaction emoji" msgstr "" #, elixir-format -#: lib/pleroma/web/common_api/common_api.ex:301 +#: lib/pleroma/web/common_api.ex:277 msgid "Could not remove reaction emoji" msgstr "" #, elixir-format -#: lib/pleroma/web/twitter_api/twitter_api.ex:129 +#: lib/pleroma/web/twitter_api/twitter_api.ex:122 msgid "Invalid CAPTCHA (Missing parameter: %{name})" msgstr "" #, elixir-format -#: lib/pleroma/web/mastodon_api/controllers/list_controller.ex:92 +#: lib/pleroma/web/mastodon_api/controllers/list_controller.ex:96 msgid "List not found" msgstr "" #, elixir-format -#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:123 +#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:151 msgid "Missing parameter: %{name}" msgstr "" #, elixir-format -#: lib/pleroma/web/oauth/oauth_controller.ex:210 -#: lib/pleroma/web/oauth/oauth_controller.ex:321 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:232 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:346 msgid "Password reset is required" msgstr "" #, elixir-format #: lib/pleroma/tests/auth_test_controller.ex:9 #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:6 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:6 -#: lib/pleroma/web/admin_api/controllers/config_controller.ex:6 lib/pleroma/web/admin_api/controllers/fallback_controller.ex:6 +#: lib/pleroma/web/admin_api/controllers/chat_controller.ex:6 lib/pleroma/web/admin_api/controllers/config_controller.ex:6 +#: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:6 lib/pleroma/web/admin_api/controllers/frontend_controller.ex:6 +#: lib/pleroma/web/admin_api/controllers/instance_controller.ex:6 lib/pleroma/web/admin_api/controllers/instance_document_controller.ex:6 #: lib/pleroma/web/admin_api/controllers/invite_controller.ex:6 lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex:6 -#: lib/pleroma/web/admin_api/controllers/oauth_app_controller.ex:6 lib/pleroma/web/admin_api/controllers/relay_controller.ex:6 +#: lib/pleroma/web/admin_api/controllers/o_auth_app_controller.ex:6 lib/pleroma/web/admin_api/controllers/relay_controller.ex:6 #: lib/pleroma/web/admin_api/controllers/report_controller.ex:6 lib/pleroma/web/admin_api/controllers/status_controller.ex:6 -#: lib/pleroma/web/controller_helper.ex:6 lib/pleroma/web/embed_controller.ex:6 -#: lib/pleroma/web/fallback_redirect_controller.ex:6 lib/pleroma/web/feed/tag_controller.ex:6 -#: lib/pleroma/web/feed/user_controller.ex:6 lib/pleroma/web/mailer/subscription_controller.ex:2 -#: lib/pleroma/web/masto_fe_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/account_controller.ex:6 -#: lib/pleroma/web/mastodon_api/controllers/app_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/auth_controller.ex:6 +#: lib/pleroma/web/admin_api/controllers/user_controller.ex:6 lib/pleroma/web/controller_helper.ex:6 lib/pleroma/web/embed_controller.ex:6 +#: lib/pleroma/web/fallback/redirect_controller.ex:6 lib/pleroma/web/feed/tag_controller.ex:6 +#: lib/pleroma/web/feed/user_controller.ex:6 lib/pleroma/web/mailer/subscription_controller.ex:6 +#: lib/pleroma/web/manifest_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/account_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/app_controller.ex:11 lib/pleroma/web/mastodon_api/controllers/auth_controller.ex:6 #: lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex:6 -#: lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:6 -#: lib/pleroma/web/mastodon_api/controllers/filter_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex:6 -#: lib/pleroma/web/mastodon_api/controllers/instance_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/list_controller.ex:6 -#: lib/pleroma/web/mastodon_api/controllers/marker_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex:14 -#: lib/pleroma/web/mastodon_api/controllers/media_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/notification_controller.ex:6 -#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/report_controller.ex:8 -#: lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/search_controller.ex:6 -#: lib/pleroma/web/mastodon_api/controllers/status_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:7 -#: lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:6 -#: lib/pleroma/web/media_proxy/media_proxy_controller.ex:6 lib/pleroma/web/mongooseim/mongoose_im_controller.ex:6 -#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:6 lib/pleroma/web/oauth/fallback_controller.ex:6 -#: lib/pleroma/web/oauth/mfa_controller.ex:10 lib/pleroma/web/oauth/oauth_controller.ex:6 -#: lib/pleroma/web/ostatus/ostatus_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/account_controller.ex:6 -#: lib/pleroma/web/pleroma_api/controllers/chat_controller.ex:5 lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex:6 -#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:2 lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex:6 -#: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/notification_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/directory_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/filter_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/instance_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/list_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/marker_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex:14 lib/pleroma/web/mastodon_api/controllers/media_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/notification_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/report_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/search_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/status_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:7 lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:6 lib/pleroma/web/media_proxy/media_proxy_controller.ex:6 +#: lib/pleroma/web/mongoose_im/mongoose_im_controller.ex:6 lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:6 +#: lib/pleroma/web/o_auth/fallback_controller.ex:6 lib/pleroma/web/o_auth/mfa_controller.ex:10 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:6 lib/pleroma/web/o_status/o_status_controller.ex:6 +#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/app_controller.ex:6 +#: lib/pleroma/web/pleroma_api/controllers/backup_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/chat_controller.ex:5 +#: lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex:6 +#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex:6 +#: lib/pleroma/web/pleroma_api/controllers/instances_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:6 +#: lib/pleroma/web/pleroma_api/controllers/notification_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/report_controller.ex:6 #: lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex:6 -#: lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex:7 lib/pleroma/web/static_fe/static_fe_controller.ex:6 +#: lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex:7 lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex:6 +#: lib/pleroma/web/static_fe/static_fe_controller.ex:6 lib/pleroma/web/twitter_api/controller.ex:6 #: lib/pleroma/web/twitter_api/controllers/password_controller.ex:10 lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex:6 -#: lib/pleroma/web/twitter_api/controllers/util_controller.ex:6 lib/pleroma/web/twitter_api/twitter_api_controller.ex:6 -#: lib/pleroma/web/uploader_controller.ex:6 lib/pleroma/web/web_finger/web_finger_controller.ex:6 +#: lib/pleroma/web/twitter_api/controllers/util_controller.ex:6 lib/pleroma/web/uploader_controller.ex:6 +#: lib/pleroma/web/web_finger/web_finger_controller.ex:6 msgid "Security violation: OAuth scopes check was neither handled nor explicitly skipped." msgstr "" #, elixir-format -#: lib/pleroma/plugs/ensure_authenticated_plug.ex:28 +#: lib/pleroma/web/plugs/ensure_authenticated_plug.ex:32 msgid "Two-factor authentication enabled, you must use a access token." msgstr "" -#, elixir-format -#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:210 -msgid "Unexpected error occurred while adding file to pack." -msgstr "" - -#, elixir-format -#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:138 -msgid "Unexpected error occurred while creating pack." -msgstr "" - -#, elixir-format -#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:278 -msgid "Unexpected error occurred while removing file from pack." -msgstr "" - -#, elixir-format -#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:250 -msgid "Unexpected error occurred while updating file in pack." -msgstr "" - -#, elixir-format -#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:179 -msgid "Unexpected error occurred while updating pack metadata." -msgstr "" - #, elixir-format #: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61 msgid "Web push subscription is disabled on this Pleroma instance" msgstr "" #, elixir-format -#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:451 +#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:234 msgid "You can't revoke your own admin/moderator status." msgstr "" #, elixir-format -#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:126 +#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:129 msgid "authorization required for timeline view" msgstr "" @@ -560,11 +514,50 @@ msgid "Access denied" msgstr "" #, elixir-format -#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:282 +#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:321 msgid "This API requires an authenticated user" msgstr "" #, elixir-format -#: lib/pleroma/plugs/user_is_admin_plug.ex:21 +#: lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex:26 +#: lib/pleroma/web/plugs/user_is_admin_plug.ex:21 msgid "User is not an admin." msgstr "" + +#, elixir-format +#: lib/pleroma/user/backup.ex:75 +msgid "Last export was less than a day ago" +msgid_plural "Last export was less than %{days} days ago" +msgstr[0] "" +msgstr[1] "" + +#, elixir-format +#: lib/pleroma/user/backup.ex:93 +msgid "Backups require enabled email" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:423 +msgid "Character limit (%{limit} characters) exceeded, contains %{length} characters" +msgstr "" + +#, elixir-format +#: lib/pleroma/user/backup.ex:98 +msgid "Email is required" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/common_api/utils.ex:507 +msgid "Too many attachments" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex:33 +#: lib/pleroma/web/plugs/user_is_staff_plug.ex:20 +msgid "User is not a staff member." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/o_auth/o_auth_controller.ex:366 +msgid "Your account is awaiting approval." +msgstr "" diff --git a/priv/gettext/posix_errors.pot b/priv/gettext/posix_errors.pot index c9f593944b..3533639e01 100644 --- a/priv/gettext/posix_errors.pot +++ b/priv/gettext/posix_errors.pot @@ -15,135 +15,135 @@ msgstr "" msgid "eagain" msgstr "" - + msgid "ebadf" msgstr "" - + msgid "ebadmsg" msgstr "" - + msgid "ebusy" msgstr "" - + msgid "edeadlk" msgstr "" - + msgid "edeadlock" msgstr "" - + msgid "edquot" msgstr "" - + msgid "eexist" msgstr "" - + msgid "efault" msgstr "" - + msgid "efbig" msgstr "" - + msgid "eftype" msgstr "" - + msgid "eintr" msgstr "" - + msgid "einval" msgstr "" - + msgid "eio" msgstr "" - + msgid "eisdir" msgstr "" - + msgid "eloop" msgstr "" - + msgid "emfile" msgstr "" - + msgid "emlink" msgstr "" - + msgid "emultihop" msgstr "" - + msgid "enametoolong" msgstr "" - + msgid "enfile" msgstr "" - + msgid "enobufs" msgstr "" - + msgid "enodev" msgstr "" - + msgid "enolck" msgstr "" - + msgid "enolink" msgstr "" - + msgid "enoent" msgstr "" - + msgid "enomem" msgstr "" - + msgid "enospc" msgstr "" - + msgid "enosr" msgstr "" - + msgid "enostr" msgstr "" - + msgid "enosys" msgstr "" - + msgid "enotblk" msgstr "" - + msgid "enotdir" msgstr "" - + msgid "enotsup" msgstr "" - + msgid "enxio" msgstr "" - + msgid "eopnotsupp" msgstr "" - + msgid "eoverflow" msgstr "" - + msgid "epipe" msgstr "" - + msgid "erange" msgstr "" - + msgid "erofs" msgstr "" - + msgid "espipe" msgstr "" - + msgid "esrch" msgstr "" - + msgid "estale" msgstr "" - + msgid "etxtbsy" msgstr "" - + msgid "exdev" msgstr "" diff --git a/priv/gettext/static_pages.pot b/priv/gettext/static_pages.pot new file mode 100644 index 0000000000..72e5c00d98 --- /dev/null +++ b/priv/gettext/static_pages.pot @@ -0,0 +1,107 @@ +## This file is a PO Template file. +## +## "msgid"s here are often extracted from source code. +## Add new translations manually only if they're dynamic +## translations that can't be statically extracted. +## +## Run "mix gettext.extract" to bring this file up to +## date. Leave "msgstr"s empty as changing them here as no +## effect: edit them in PO (.po) files instead. +msgid "" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex:9 +msgctxt "remote follow authorization button" +msgid "Authorize" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex:2 +msgctxt "remote follow error" +msgid "Error fetching user" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex:4 +msgctxt "remote follow header" +msgid "Remote follow" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex:8 +msgctxt "placeholder text for auth code entry" +msgid "Authentication code" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:10 +msgctxt "placeholder text for password entry" +msgid "Password" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:8 +msgctxt "placeholder text for username entry" +msgid "Username" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:13 +msgctxt "remote follow authorization button for login" +msgid "Authorize" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex:12 +msgctxt "remote follow authorization button for mfa" +msgid "Authorize" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex:2 +msgctxt "remote follow error" +msgid "Error following account" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:4 +msgctxt "remote follow header, need login" +msgid "Log in to follow" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex:4 +msgctxt "remote follow mfa header" +msgid "Two-factor authentication" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex:4 +msgctxt "remote follow success" +msgid "Account followed!" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:7 +msgctxt "placeholder text for account id" +msgid "Your account ID, e.g. lain@quitter.se" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:8 +msgctxt "remote follow authorization button for following with a remote account" +msgid "Follow" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:2 +msgctxt "remote follow error" +msgid "Error: %{error}" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:4 +msgctxt "remote follow header" +msgid "Remotely follow %{nickname}" +msgstr "" From 9f4c5743e842591b78744d4effae86cde4485d31 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Mon, 21 Feb 2022 19:12:32 -0500 Subject: [PATCH 04/37] Make lint happy --- lib/pleroma/web/plugs/set_locale_plug.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/plugs/set_locale_plug.ex b/lib/pleroma/web/plugs/set_locale_plug.ex index 446baf24b8..a9387ba7e1 100644 --- a/lib/pleroma/web/plugs/set_locale_plug.ex +++ b/lib/pleroma/web/plugs/set_locale_plug.ex @@ -6,7 +6,7 @@ defmodule Pleroma.Web.Plugs.SetLocalePlug do import Plug.Conn, only: [get_req_header: 2, assign: 3] - def frontend_language_cookie_name(), do: "userLanguage" + def frontend_language_cookie_name, do: "userLanguage" def init(_), do: nil From 56197192bb03058fda375767cc8ffc35c0c9eed0 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 25 Feb 2022 10:31:42 +0100 Subject: [PATCH 05/37] mix: Check .git presence --- mix.exs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 4387cb0aaa..7893b8438c 100644 --- a/mix.exs +++ b/mix.exs @@ -243,9 +243,10 @@ defp version(version) do identifier_filter = ~r/[^0-9a-z\-]+/i git_available? = match?({_output, 0}, System.cmd("sh", ["-c", "command -v git"])) + dotgit_present? = File.exists?(".git") git_pre_release = - if git_available? do + if git_available? and dotgit_present? do {tag, tag_err} = System.cmd("git", ["describe", "--tags", "--abbrev=0"], stderr_to_stdout: true) @@ -272,6 +273,7 @@ defp version(version) do # Branch name as pre-release version component, denoted with a dot branch_name = with true <- git_available?, + true <- dotgit_present?, {branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]), branch_name <- String.trim(branch_name), branch_name <- System.get_env("PLEROMA_BUILD_BRANCH") || branch_name, From 1edbda39e15b3861229283806dcbbf637f9cd753 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Mon, 28 Feb 2022 01:04:04 -0500 Subject: [PATCH 06/37] Make password reset pages translatable --- .../password/invalid_token.html.eex | 2 +- .../twitter_api/password/reset.html.eex | 6 +-- .../password/reset_failed.html.eex | 8 +++- .../password/reset_success.html.eex | 4 +- .../web/twitter_api/views/password_view.ex | 1 + priv/gettext/static_pages.pot | 48 +++++++++++++++++++ 6 files changed, 61 insertions(+), 8 deletions(-) diff --git a/lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex b/lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex index ee84750c7d..5ac0aa4e0e 100644 --- a/lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex +++ b/lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex @@ -1 +1 @@ -

Invalid Token

+

<%= Gettext.dpgettext("static_pages", "password reset invalid token message", "Invalid Token") %>

diff --git a/lib/pleroma/web/templates/twitter_api/password/reset.html.eex b/lib/pleroma/web/templates/twitter_api/password/reset.html.eex index fbcacdc148..6a544af514 100644 --- a/lib/pleroma/web/templates/twitter_api/password/reset.html.eex +++ b/lib/pleroma/web/templates/twitter_api/password/reset.html.eex @@ -1,13 +1,13 @@

Password Reset for <%= @user.nickname %>

<%= form_for @conn, Routes.reset_password_path(@conn, :do_reset), [as: "data"], fn f -> %>
- <%= label f, :password, "Password" %> + <%= label f, :password, Gettext.dpgettext("static_pages", "password reset form password prompt", "Password") %> <%= password_input f, :password %>
- <%= label f, :password_confirmation, "Confirmation" %> + <%= label f, :password_confirmation, Gettext.dpgettext("static_pages", "password reset form confirm password prompt", "Confirmation") %> <%= password_input f, :password_confirmation %>
<%= hidden_input f, :token, value: @token.token %> - <%= submit "Reset" %> + <%= submit Gettext.dpgettext("static_pages", "password reset button", "Reset") %> <% end %> diff --git a/lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex b/lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex index 4ed4ac8bce..774e3462aa 100644 --- a/lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex +++ b/lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex @@ -1,2 +1,6 @@ -

Password reset failed

-

Homepage

+

<%= Gettext.dpgettext("static_pages", "password reset failed message", "Password reset failed") %>

+

+ + <%= Gettext.dpgettext("static_pages", "password reset failed homepage link", "Homepage") %> + +

diff --git a/lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex b/lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex index 086d4e08b6..40f6bb3fcb 100644 --- a/lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex +++ b/lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex @@ -1,2 +1,2 @@ -

Password changed!

-

Homepage

+

<%= Gettext.dpgettext("static_pages", "password reset successful message", "Password changed!") %>

+

<%= Gettext.dpgettext("static_pages", "password reset successful homepage link", "Homepage") %>

diff --git a/lib/pleroma/web/twitter_api/views/password_view.ex b/lib/pleroma/web/twitter_api/views/password_view.ex index a9bb95a2cc..40e7fca49e 100644 --- a/lib/pleroma/web/twitter_api/views/password_view.ex +++ b/lib/pleroma/web/twitter_api/views/password_view.ex @@ -5,4 +5,5 @@ defmodule Pleroma.Web.TwitterAPI.PasswordView do use Pleroma.Web, :view import Phoenix.HTML.Form + alias Pleroma.Web.Gettext end diff --git a/priv/gettext/static_pages.pot b/priv/gettext/static_pages.pot index 72e5c00d98..8b56ad7b42 100644 --- a/priv/gettext/static_pages.pot +++ b/priv/gettext/static_pages.pot @@ -105,3 +105,51 @@ msgstr "" msgctxt "remote follow header" msgid "Remotely follow %{nickname}" msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/password/reset.html.eex:12 +msgctxt "password reset button" +msgid "Reset" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex:4 +msgctxt "password reset failed homepage link" +msgid "Homepage" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex:1 +msgctxt "password reset failed message" +msgid "Password reset failed" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/password/reset.html.eex:8 +msgctxt "password reset form confirm password prompt" +msgid "Confirmation" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/password/reset.html.eex:4 +msgctxt "password reset form password prompt" +msgid "Password" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex:1 +msgctxt "password reset invalid token message" +msgid "Invalid Token" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex:2 +msgctxt "password reset successful homepage link" +msgid "Homepage" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex:1 +msgctxt "password reset successful message" +msgid "Password changed!" +msgstr "" From 0cc655771642cd840e436a0622e110e69e745338 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Mon, 28 Feb 2022 01:13:39 -0500 Subject: [PATCH 07/37] Make tag feed translatable --- lib/pleroma/web/feed/feed_view.ex | 1 + lib/pleroma/web/templates/feed/feed/tag.atom.eex | 2 +- lib/pleroma/web/templates/feed/feed/tag.rss.eex | 2 +- priv/gettext/static_pages.pot | 7 +++++++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex index c0fb35e01a..d674bc26fe 100644 --- a/lib/pleroma/web/feed/feed_view.ex +++ b/lib/pleroma/web/feed/feed_view.ex @@ -10,6 +10,7 @@ defmodule Pleroma.Web.Feed.FeedView do alias Pleroma.Object alias Pleroma.User alias Pleroma.Web.MediaProxy + alias Pleroma.Web.Gettext require Pleroma.Constants diff --git a/lib/pleroma/web/templates/feed/feed/tag.atom.eex b/lib/pleroma/web/templates/feed/feed/tag.atom.eex index de07310856..2d860f12be 100644 --- a/lib/pleroma/web/templates/feed/feed/tag.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/tag.atom.eex @@ -12,7 +12,7 @@ <%= '#{Routes.tag_feed_url(@conn, :feed, @tag)}.rss' %> #<%= @tag %> - These are public toots tagged with #<%= @tag %>. You can interact with them if you have an account anywhere in the fediverse. + <%= Gettext.dpgettext("static_pages", "tag feed description", "These are public toots tagged with #%{tag}. You can interact with them if you have an account anywhere in the fediverse.", tag: @tag) %> <%= feed_logo() %> <%= most_recent_update(@activities) %> diff --git a/lib/pleroma/web/templates/feed/feed/tag.rss.eex b/lib/pleroma/web/templates/feed/feed/tag.rss.eex index 9c3613febc..edcc3e436d 100644 --- a/lib/pleroma/web/templates/feed/feed/tag.rss.eex +++ b/lib/pleroma/web/templates/feed/feed/tag.rss.eex @@ -4,7 +4,7 @@ #<%= @tag %> - These are public toots tagged with #<%= @tag %>. You can interact with them if you have an account anywhere in the fediverse. + <%= Gettext.dpgettext("static_pages", "tag feed description", "These are public toots tagged with #%{tag}. You can interact with them if you have an account anywhere in the fediverse.", tag: @tag) %> <%= '#{Routes.tag_feed_url(@conn, :feed, @tag)}.rss' %> <%= feed_logo() %> 2b90d9 diff --git a/priv/gettext/static_pages.pot b/priv/gettext/static_pages.pot index 8b56ad7b42..b230fbaa7a 100644 --- a/priv/gettext/static_pages.pot +++ b/priv/gettext/static_pages.pot @@ -153,3 +153,10 @@ msgstr "" msgctxt "password reset successful message" msgid "Password changed!" msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/feed/feed/tag.atom.eex:15 +#: lib/pleroma/web/templates/feed/feed/tag.rss.eex:7 +msgctxt "tag feed description" +msgid "These are public toots tagged with #%{tag}. You can interact with them if you have an account anywhere in the fediverse." +msgstr "" From f63d9b7835757d28860286f96d22f54196aeb46a Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Mon, 28 Feb 2022 01:28:23 -0500 Subject: [PATCH 08/37] Use proper lang attributes in htmls --- lib/pleroma/web/gettext.ex | 9 +++++++++ lib/pleroma/web/templates/feed/feed/tag.atom.eex | 2 +- lib/pleroma/web/templates/layout/app.html.eex | 2 +- lib/pleroma/web/templates/layout/email.html.eex | 4 ++-- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/pleroma/web/gettext.ex b/lib/pleroma/web/gettext.ex index c0ca4d0e9e..c8a739c2bd 100644 --- a/lib/pleroma/web/gettext.ex +++ b/lib/pleroma/web/gettext.ex @@ -25,4 +25,13 @@ defmodule Pleroma.Web.Gettext do See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage. """ use Gettext, otp_app: :pleroma + + def language_tag do + # Naive implementation: HTML lang attribute uses BCP 47, which + # uses - as a separator. + # https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang + + Gettext.get_locale() + |> String.replace("_", "-", global: true) + end end diff --git a/lib/pleroma/web/templates/feed/feed/tag.atom.eex b/lib/pleroma/web/templates/feed/feed/tag.atom.eex index 2d860f12be..6d497e84c2 100644 --- a/lib/pleroma/web/templates/feed/feed/tag.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/tag.atom.eex @@ -1,6 +1,6 @@ - - + diff --git a/lib/pleroma/web/templates/layout/email.html.eex b/lib/pleroma/web/templates/layout/email.html.eex index f6dcd7f0fc..087aa4fc01 100644 --- a/lib/pleroma/web/templates/layout/email.html.eex +++ b/lib/pleroma/web/templates/layout/email.html.eex @@ -1,5 +1,5 @@ - + <%= @email.subject %> @@ -7,4 +7,4 @@ <%= render @view_module, @view_template, assigns %> - \ No newline at end of file + From 50a316cd63c4c4e9462fb64c2f452f6e645840b4 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Mon, 28 Feb 2022 02:11:57 -0500 Subject: [PATCH 09/37] Make oauth pages translatable --- lib/pleroma/web/o_auth/o_auth_view.ex | 2 + .../templates/o_auth/o_auth/_scopes.html.eex | 2 +- .../templates/o_auth/o_auth/consumer.html.eex | 4 +- .../o_auth/oob_authorization_created.html.eex | 4 +- .../o_auth/o_auth/oob_token_exists.html.eex | 4 +- .../templates/o_auth/o_auth/register.html.eex | 18 +-- .../web/templates/o_auth/o_auth/show.html.eex | 20 +-- priv/gettext/static_pages.pot | 145 ++++++++++++++++++ 8 files changed, 174 insertions(+), 25 deletions(-) diff --git a/lib/pleroma/web/o_auth/o_auth_view.ex b/lib/pleroma/web/o_auth/o_auth_view.ex index 1419c96a2f..57a315705f 100644 --- a/lib/pleroma/web/o_auth/o_auth_view.ex +++ b/lib/pleroma/web/o_auth/o_auth_view.ex @@ -5,6 +5,8 @@ defmodule Pleroma.Web.OAuth.OAuthView do use Pleroma.Web, :view import Phoenix.HTML.Form + import Phoenix.HTML + alias Pleroma.Web.Gettext alias Pleroma.Web.OAuth.Token.Utils diff --git a/lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex index c9ec1ecbfa..73115e92a2 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex @@ -1,5 +1,5 @@
- <%= label @form, :scope, "The following permissions will be granted" %> + <%= label @form, :scope, Gettext.dpgettext("static_pages", "oauth scopes message", "The following permissions will be granted") %>
<%= for scope <- @available_scopes do %> <%# Note: using hidden input with `unchecked_value` in order to distinguish user's empty selection from `scope` param being omitted %> diff --git a/lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex index dc4521a62b..8b894cd58a 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex @@ -1,4 +1,4 @@ -

Sign in with external provider

+

<%= Gettext.dpgettext("static_pages", "oauth external provider page title", "Sign in with external provider") %>

<%= form_for @conn, Routes.o_auth_path(@conn, :prepare_request), [as: "authorization", method: "get"], fn f -> %>
@@ -10,6 +10,6 @@ <%= hidden_input f, :state, value: @state %> <%= for strategy <- Pleroma.Config.oauth_consumer_strategies() do %> - <%= submit "Sign in with #{String.capitalize(strategy)}", name: "provider", value: strategy %> + <%= submit Gettext.dpgettext("static_pages", "oauth external provider sign in button", "Sign in with %{strategy}", strategy: String.capitalize(strategy)), name: "provider", value: strategy %> <% end %> <% end %> diff --git a/lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex index ffabe29a62..aaa38513ad 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex @@ -1,2 +1,2 @@ -

Successfully authorized

-

Token code is
<%= @auth.token %>

+

<%= Gettext.dpgettext("static_pages", "oauth authorized page title", "Successfully authorized") %>

+

<%= raw Gettext.dpgettext("static_pages", "oauth token code message", "Token code is
%{token}", token: html_escape(@auth.token)) %>

diff --git a/lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex index 82785c4b99..de4cd0f343 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex @@ -1,2 +1,2 @@ -

Authorization exists

-

Access token is
<%= @token.token %>

+

<%= Gettext.dpgettext("static_pages", "oauth authorization exists page title", "Authorization exists") %>

+

<%= raw Gettext.dpgettext("static_pages", "oauth token code message", "Token code is
%{token}", token: html_escape(@auth.token)) %>

diff --git a/lib/pleroma/web/templates/o_auth/o_auth/register.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/register.html.eex index 3ac428b2fb..1f661efb21 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/register.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/register.html.eex @@ -5,34 +5,34 @@ <% end %> -

Registration Details

+

<%= Gettext.dpgettext("static_pages", "oauth register page title", "Registration Details") %>

-

If you'd like to register a new account, please provide the details below.

+

<%= Gettext.dpgettext("static_pages", "oauth register page fill form prompt", "If you'd like to register a new account, please provide the details below.") %>

<%= form_for @conn, Routes.o_auth_path(@conn, :register), [as: "authorization"], fn f -> %>
- <%= label f, :nickname, "Nickname" %> + <%= label f, :nickname, Gettext.dpgettext("static_pages", "oauth register page nickname prompt", "Nickname") %> <%= text_input f, :nickname, value: @nickname, autocomplete: "username" %>
- <%= label f, :email, "Email" %> + <%= label f, :email, Gettext.dpgettext("static_pages", "oauth register page email prompt", "Email") %> <%= text_input f, :email, value: @email, autocomplete: "email" %>
-<%= submit "Proceed as new user", name: "op", value: "register" %> +<%= submit Gettext.dpgettext("static_pages", "oauth register page register button", "Proceed as new user"), name: "op", value: "register" %> -

Alternatively, sign in to connect to existing account.

+

<%= Gettext.dpgettext("static_pages", "oauth register page login prompt", "Alternatively, sign in to connect to existing account.") %>

- <%= label f, :name, "Name or email" %> + <%= label f, :name, Gettext.dpgettext("static_pages", "oauth register page login username prompt", "Name or email") %> <%= text_input f, :name, autocomplete: "username" %>
- <%= label f, :password, "Password" %> + <%= label f, :password, Gettext.dpgettext("static_pages", "oauth register page login password prompt", "Password") %> <%= password_input f, :password, autocomplete: "password" %>
-<%= submit "Proceed as existing user", name: "op", value: "connect" %> +<%= submit Gettext.dpgettext("static_pages", "oauth register page login button", "Proceed as existing user"), name: "op", value: "connect" %> <%= hidden_input f, :client_id, value: @client_id %> <%= hidden_input f, :redirect_uri, value: @redirect_uri %> diff --git a/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex index d63da6c1dd..31ae3cd1ba 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex @@ -20,21 +20,23 @@
<%= if @app do %> -

Application <%= @app.client_name %> is requesting access to your account.

+

<%= raw Gettext.dpgettext("static_pages", "oauth authorize message", "Application %{client_name} is requesting access to your account.", client_name: html_escape(@app.client_name)) %>

<%= render @view_module, "_scopes.html", Map.merge(assigns, %{form: f}) %> <% end %> <%= if @user do %>
- Cancel - <%= submit "Approve", class: "button--approve" %> + + <%= Gettext.dpgettext("static_pages", "oauth authorize cancel button", "Cancel") %> + + <%= submit Gettext.dpgettext("static_pages", "oauth authorize approve button", "Approve"), class: "button--approve" %>
<% else %> <%= if @params["registration"] in ["true", true] do %> -

This is the first time you visit! Please enter your Pleroma handle.

-

Choose carefully! You won't be able to change this later. You will be able to change your display name, though.

+

<%= Gettext.dpgettext("static_pages", "oauth register page title", "This is the first time you visit! Please enter your Pleroma handle.") %>

+

<%= Gettext.dpgettext("static_pages", "oauth register nickname unchangeable warning", "Choose carefully! You won't be able to change this later. You will be able to change your display name, though.") %>

- <%= label f, :nickname, "Pleroma Handle" %> + <%= label f, :nickname, Gettext.dpgettext("static_pages", "oauth register nickname prompt", "Pleroma Handle") %> <%= text_input f, :nickname, placeholder: "lain", autocomplete: "username" %>
<%= hidden_input f, :name, value: @params["name"] %> @@ -42,14 +44,14 @@
<% else %>
- <%= label f, :name, "Username" %> + <%= label f, :name, Gettext.dpgettext("static_pages", "oauth login username prompt", "Username") %> <%= text_input f, :name %>
- <%= label f, :password, "Password" %> + <%= label f, :password, Gettext.dpgettext("static_pages", "oauth login password prompt", "Password") %> <%= password_input f, :password %>
- <%= submit "Log In" %> + <%= submit Gettext.dpgettext("static_pages", "oauth login button", "Log In") %> <% end %> <% end %>
diff --git a/priv/gettext/static_pages.pot b/priv/gettext/static_pages.pot index b230fbaa7a..f9d535838d 100644 --- a/priv/gettext/static_pages.pot +++ b/priv/gettext/static_pages.pot @@ -160,3 +160,148 @@ msgstr "" msgctxt "tag feed description" msgid "These are public toots tagged with #%{tag}. You can interact with them if you have an account anywhere in the fediverse." msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex:1 +msgctxt "oauth authorization exists page title" +msgid "Authorization exists" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:32 +msgctxt "oauth authorize approve button" +msgid "Approve" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:30 +msgctxt "oauth authorize cancel button" +msgid "Cancel" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:23 +msgctxt "oauth authorize message" +msgid "Application %{client_name} is requesting access to your account." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex:1 +msgctxt "oauth authorized page title" +msgid "Successfully authorized" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex:1 +msgctxt "oauth external provider page title" +msgid "Sign in with external provider" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex:13 +msgctxt "oauth external provider sign in button" +msgid "Sign in with %{strategy}" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:54 +msgctxt "oauth login button" +msgid "Log In" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:51 +msgctxt "oauth login password prompt" +msgid "Password" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:47 +msgctxt "oauth login username prompt" +msgid "Username" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:39 +msgctxt "oauth register nickname prompt" +msgid "Pleroma Handle" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:37 +msgctxt "oauth register nickname unchangeable warning" +msgid "Choose carefully! You won't be able to change this later. You will be able to change your display name, though." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:18 +msgctxt "oauth register page email prompt" +msgid "Email" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:10 +msgctxt "oauth register page fill form prompt" +msgid "If you'd like to register a new account, please provide the details below." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:35 +msgctxt "oauth register page login button" +msgid "Proceed as existing user" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:31 +msgctxt "oauth register page login password prompt" +msgid "Password" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:24 +msgctxt "oauth register page login prompt" +msgid "Alternatively, sign in to connect to existing account." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:27 +msgctxt "oauth register page login username prompt" +msgid "Name or email" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:14 +msgctxt "oauth register page nickname prompt" +msgid "Nickname" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:22 +msgctxt "oauth register page register button" +msgid "Proceed as new user" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:8 +msgctxt "oauth register page title" +msgid "Registration Details" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:36 +msgctxt "oauth register page title" +msgid "This is the first time you visit! Please enter your Pleroma handle." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex:2 +msgctxt "oauth scopes message" +msgid "The following permissions will be granted" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex:2 +#: lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex:2 +msgctxt "oauth token code message" +msgid "Token code is
%{token}" +msgstr "" From cadca083ea2a12a0e97fbd5e0e4fbae26b5d79a3 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Mon, 28 Feb 2022 11:07:28 -0500 Subject: [PATCH 10/37] Make mfa pages translatable --- lib/pleroma/web/o_auth/mfa_view.ex | 1 + .../templates/o_auth/mfa/recovery.html.eex | 8 ++-- .../web/templates/o_auth/mfa/totp.html.eex | 8 ++-- priv/gettext/static_pages.pot | 48 +++++++++++++++++++ 4 files changed, 57 insertions(+), 8 deletions(-) diff --git a/lib/pleroma/web/o_auth/mfa_view.ex b/lib/pleroma/web/o_auth/mfa_view.ex index 3d473f29c4..952c90efef 100644 --- a/lib/pleroma/web/o_auth/mfa_view.ex +++ b/lib/pleroma/web/o_auth/mfa_view.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.OAuth.MFAView do use Pleroma.Web, :view import Phoenix.HTML.Form alias Pleroma.MFA + alias Pleroma.Web.Gettext def render("mfa_response.json", %{token: token, user: user}) do %{ diff --git a/lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex b/lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex index b9daa8d8b5..e45d13bdfa 100644 --- a/lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex +++ b/lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex @@ -5,11 +5,11 @@ <% end %> -

Two-factor recovery

+

<%= Gettext.dpgettext("static_pages", "mfa recover page title", "Two-factor recovery") %>

<%= form_for @conn, Routes.mfa_verify_path(@conn, :verify), [as: "mfa"], fn f -> %>
- <%= label f, :code, "Recovery code" %> + <%= label f, :code, Gettext.dpgettext("static_pages", "mfa recover recovery code prompt", "Recovery code") %> <%= text_input f, :code, [autocomplete: false, autocorrect: "off", autocapitalize: "off", autofocus: true, spellcheck: false] %> <%= hidden_input f, :mfa_token, value: @mfa_token %> <%= hidden_input f, :state, value: @state %> @@ -17,8 +17,8 @@ <%= hidden_input f, :challenge_type, value: "recovery" %>
-<%= submit "Verify" %> +<%= submit Gettext.dpgettext("static_pages", "mfa recover verify recovery code button", "Verify") %> <% end %> "> - Enter a two-factor code + <%= Gettext.dpgettext("static_pages", "mfa recover use 2fa code link", "Enter a two-factor code") %> diff --git a/lib/pleroma/web/templates/o_auth/mfa/totp.html.eex b/lib/pleroma/web/templates/o_auth/mfa/totp.html.eex index 27600253c3..50e6c04b64 100644 --- a/lib/pleroma/web/templates/o_auth/mfa/totp.html.eex +++ b/lib/pleroma/web/templates/o_auth/mfa/totp.html.eex @@ -5,11 +5,11 @@ <% end %> -

Two-factor authentication

+

<%= Gettext.dpgettext("static_pages", "mfa auth page title", "Two-factor authentication") %>

<%= form_for @conn, Routes.mfa_verify_path(@conn, :verify), [as: "mfa"], fn f -> %>
- <%= label f, :code, "Authentication code" %> + <%= label f, :code, Gettext.dpgettext("static_pages", "mfa auth code prompt", "Authentication code") %> <%= text_input f, :code, [autocomplete: "one-time-code", autocorrect: "off", autocapitalize: "off", autofocus: true, pattern: "[0-9]*", spellcheck: false] %> <%= hidden_input f, :mfa_token, value: @mfa_token %> <%= hidden_input f, :state, value: @state %> @@ -17,8 +17,8 @@ <%= hidden_input f, :challenge_type, value: "totp" %>
-<%= submit "Verify" %> +<%= submit Gettext.dpgettext("static_pages", "mfa auth verify code button", "Verify") %> <% end %> "> - Enter a two-factor recovery code + <%= Gettext.dpgettext("static_pages", "mfa auth page use recovery code link", "Enter a two-factor recovery code") %> diff --git a/priv/gettext/static_pages.pot b/priv/gettext/static_pages.pot index f9d535838d..1af6796c84 100644 --- a/priv/gettext/static_pages.pot +++ b/priv/gettext/static_pages.pot @@ -305,3 +305,51 @@ msgstr "" msgctxt "oauth token code message" msgid "Token code is
%{token}" msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:12 +msgctxt "mfa auth code prompt" +msgid "Authentication code" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:8 +msgctxt "mfa auth page title" +msgid "Two-factor authentication" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:23 +msgctxt "mfa auth page use recovery code link" +msgid "Enter a two-factor recovery code" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:20 +msgctxt "mfa auth verify code button" +msgid "Verify" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:8 +msgctxt "mfa recover page title" +msgid "Two-factor recovery" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:12 +msgctxt "mfa recover recovery code prompt" +msgid "Recovery code" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:23 +msgctxt "mfa recover use 2fa code link" +msgid "Enter a two-factor code" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:20 +msgctxt "mfa recover verify recovery code button" +msgid "Verify" +msgstr "" From fdbf9b06e5672cf6e6038165191fae9bf01bee9f Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Mon, 28 Feb 2022 11:23:15 -0500 Subject: [PATCH 11/37] Fix tests --- lib/pleroma/web/feed/feed_view.ex | 2 +- .../templates/o_auth/o_auth/oob_authorization_created.html.eex | 2 +- .../web/templates/o_auth/o_auth/oob_token_exists.html.eex | 2 +- lib/pleroma/web/templates/o_auth/o_auth/show.html.eex | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex index d674bc26fe..52771205ec 100644 --- a/lib/pleroma/web/feed/feed_view.ex +++ b/lib/pleroma/web/feed/feed_view.ex @@ -9,8 +9,8 @@ defmodule Pleroma.Web.Feed.FeedView do alias Pleroma.Formatter alias Pleroma.Object alias Pleroma.User - alias Pleroma.Web.MediaProxy alias Pleroma.Web.Gettext + alias Pleroma.Web.MediaProxy require Pleroma.Constants diff --git a/lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex index aaa38513ad..76ed3fda5e 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex @@ -1,2 +1,2 @@

<%= Gettext.dpgettext("static_pages", "oauth authorized page title", "Successfully authorized") %>

-

<%= raw Gettext.dpgettext("static_pages", "oauth token code message", "Token code is
%{token}", token: html_escape(@auth.token)) %>

+

<%= raw Gettext.dpgettext("static_pages", "oauth token code message", "Token code is
%{token}", token: safe_to_string(html_escape(@auth.token))) %>

diff --git a/lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex index de4cd0f343..754bf2eb09 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex @@ -1,2 +1,2 @@

<%= Gettext.dpgettext("static_pages", "oauth authorization exists page title", "Authorization exists") %>

-

<%= raw Gettext.dpgettext("static_pages", "oauth token code message", "Token code is
%{token}", token: html_escape(@auth.token)) %>

+

<%= raw Gettext.dpgettext("static_pages", "oauth token code message", "Token code is
%{token}", token: safe_to_string(html_escape(@token.token))) %>

diff --git a/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex index 31ae3cd1ba..a2f41618e6 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex @@ -20,7 +20,7 @@
<%= if @app do %> -

<%= raw Gettext.dpgettext("static_pages", "oauth authorize message", "Application %{client_name} is requesting access to your account.", client_name: html_escape(@app.client_name)) %>

+

<%= raw Gettext.dpgettext("static_pages", "oauth authorize message", "Application %{client_name} is requesting access to your account.", client_name: safe_to_string(html_escape(@app.client_name))) %>

<%= render @view_module, "_scopes.html", Map.merge(assigns, %{form: f}) %> <% end %> From 32e4aa42d395fceb592b783cdca1268835fe41b5 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 1 Mar 2022 18:48:08 -0500 Subject: [PATCH 12/37] Make static fe translatable --- .../web/templates/static_fe/static_fe/profile.html.eex | 2 +- priv/gettext/static_pages.pot | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex b/lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex index 3191bf4504..a14ca305ef 100644 --- a/lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex +++ b/lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex @@ -5,7 +5,7 @@
- +
<%= raw Formatter.emojify(@user.name, @user.emoji) %> | <%= link "@#{@user.nickname}@#{Endpoint.host()}", to: (@user.uri || @user.ap_id) %> diff --git a/priv/gettext/static_pages.pot b/priv/gettext/static_pages.pot index 1af6796c84..1bde0099db 100644 --- a/priv/gettext/static_pages.pot +++ b/priv/gettext/static_pages.pot @@ -353,3 +353,9 @@ msgstr "" msgctxt "mfa recover verify recovery code button" msgid "Verify" msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex:8 +msgctxt "static fe profile page remote follow button" +msgid "Remote follow" +msgstr "" From 1deab33fb0beee8803d3673f86ae7b0dca8eec80 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 1 Mar 2022 19:17:11 -0500 Subject: [PATCH 13/37] Make mail and mailer translatable --- .../web/templates/email/digest.html.eex | 10 ++-- .../subscription/unsubscribe_failure.html.eex | 2 +- .../subscription/unsubscribe_success.html.eex | 2 +- lib/pleroma/web/views/email_view.ex | 1 + .../web/views/mailer/subscription_view.ex | 1 + priv/gettext/static_pages.pot | 50 +++++++++++++++++++ 6 files changed, 59 insertions(+), 7 deletions(-) diff --git a/lib/pleroma/web/templates/email/digest.html.eex b/lib/pleroma/web/templates/email/digest.html.eex index 60eceff221..1efc76e1ad 100644 --- a/lib/pleroma/web/templates/email/digest.html.eex +++ b/lib/pleroma/web/templates/email/digest.html.eex @@ -160,7 +160,7 @@

Hey <%= @user.nickname %>, here is what you've missed!

+ style="font-size: 30px; color: <%= @styling.header_color %>;"><%= Gettext.dpgettext("static_pages", "digest email header line", "Hey %{nickname}, here is what you've missed!", nickname: @user.nickname) %>

@@ -382,7 +382,7 @@

<%= length(@followers) %> New Followers<%= Gettext.dpngettext("static_pages", "new followers count header", "%{count} New Follower", "%{count} New Followers", length(@followers), count: length(@followers)) %>

@@ -535,16 +535,16 @@ style="color:<%= @styling.text_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">

- You have received this email because you have signed up to receive digest emails from <%= @instance %> Pleroma instance.

+ <%= raw Gettext.dpgettext("static_pages", "digest email sending reason", "You have received this email because you have signed up to receive digest emails from %{instance} Pleroma instance.", instance: safe_to_string(html_escape(@instance))) %>

 

- The email address you are subscribed as is <%= @user.email %>.

+ <%= raw Gettext.dpgettext("static_pages", "digest email receiver address", "The email address you are subscribed as is %{email}. ", color: safe_to_string(html_escape(@styling.link_color)), email: safe_to_string(html_escape(@user.email))) %>

- To unsubscribe, please go <%= link "here", style: "color: #{@styling.link_color};text-decoration: none;", to: @unsubscribe_link %>.

+ <%= raw Gettext.dpgettext("static_pages", "digest email unsubscribe action", "To unsubscribe, please go %{here}.", here: safe_to_string link(Gettext.dpgettext("static_pages", "digest email unsubscribe action link text", "here"), style: "color: #{@styling.link_color};text-decoration: none;", to: @unsubscribe_link)) %>

diff --git a/lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex b/lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex index 7b476f02dd..df090ffcdd 100644 --- a/lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex +++ b/lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex @@ -1 +1 @@ -

UNSUBSCRIBE FAILURE

+

<%= Gettext.dpgettext("static_pages", "mailer unsubscribe failed message", "UNSUBSCRIBE FAILURE") %>

diff --git a/lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex b/lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex index 6dfa2c1859..cbce495d44 100644 --- a/lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex +++ b/lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex @@ -1 +1 @@ -

UNSUBSCRIBE SUCCESSFUL

+

<%= Gettext.dpgettext("static_pages", "mailer unsubscribe successful message", "UNSUBSCRIBE SUCCESSFUL") %>

diff --git a/lib/pleroma/web/views/email_view.ex b/lib/pleroma/web/views/email_view.ex index f7659b994f..2ef049d27b 100644 --- a/lib/pleroma/web/views/email_view.ex +++ b/lib/pleroma/web/views/email_view.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.EmailView do use Pleroma.Web, :view import Phoenix.HTML import Phoenix.HTML.Link + alias Pleroma.Web.Gettext def avatar_url(user) do Pleroma.User.avatar_url(user) diff --git a/lib/pleroma/web/views/mailer/subscription_view.ex b/lib/pleroma/web/views/mailer/subscription_view.ex index 1dc80987b9..01e96c61cc 100644 --- a/lib/pleroma/web/views/mailer/subscription_view.ex +++ b/lib/pleroma/web/views/mailer/subscription_view.ex @@ -4,4 +4,5 @@ defmodule Pleroma.Web.Mailer.SubscriptionView do use Pleroma.Web, :view + alias Pleroma.Web.Gettext end diff --git a/priv/gettext/static_pages.pot b/priv/gettext/static_pages.pot index 1bde0099db..b76641e283 100644 --- a/priv/gettext/static_pages.pot +++ b/priv/gettext/static_pages.pot @@ -359,3 +359,53 @@ msgstr "" msgctxt "static fe profile page remote follow button" msgid "Remote follow" msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/email/digest.html.eex:163 +msgctxt "digest email header line" +msgid "Hey %{nickname}, here is what you've missed!" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/email/digest.html.eex:544 +msgctxt "digest email receiver address" +msgid "The email address you are subscribed as is %{email}. " +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/email/digest.html.eex:538 +msgctxt "digest email sending reason" +msgid "You have received this email because you have signed up to receive digest emails from %{instance} Pleroma instance." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/email/digest.html.eex:547 +msgctxt "digest email unsubscribe action" +msgid "To unsubscribe, please go %{here}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/email/digest.html.eex:547 +msgctxt "digest email unsubscribe action link text" +msgid "here" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex:1 +msgctxt "mailer unsubscribe failed message" +msgid "UNSUBSCRIBE FAILURE" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex:1 +msgctxt "mailer unsubscribe successful message" +msgid "UNSUBSCRIBE SUCCESSFUL" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/email/digest.html.eex:385 +msgctxt "new followers count header" +msgid "%{count} New Follower" +msgid_plural "%{count} New Followers" +msgstr[0] "" +msgstr[1] "" From 8b0c2890f99feae374628d96ae1e65949e7631f5 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 1 Mar 2022 20:27:45 -0500 Subject: [PATCH 14/37] Fix digest test --- test/mix/tasks/pleroma/digest_test.exs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/mix/tasks/pleroma/digest_test.exs b/test/mix/tasks/pleroma/digest_test.exs index 4a9e461a9f..b8050c7afe 100644 --- a/test/mix/tasks/pleroma/digest_test.exs +++ b/test/mix/tasks/pleroma/digest_test.exs @@ -53,7 +53,13 @@ test "Sends digest to the given user" do assert_email_sent( to: {user2.name, user2.email}, - html_body: ~r/here is what you've missed!/i + html_body: + Regex.compile!( + "here is what you've missed!" + |> Phoenix.HTML.html_escape() + |> Phoenix.HTML.safe_to_string(), + "i" + ) ) end end From af82f09ce39dcc36498549be00f52c530936d0cd Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 1 Mar 2022 20:29:26 -0500 Subject: [PATCH 15/37] Make all emails translatable --- lib/pleroma/emails/user_email.ex | 207 ++++++++++++++++++++++++------- priv/gettext/static_pages.pot | 102 +++++++++++++++ 2 files changed, 266 insertions(+), 43 deletions(-) diff --git a/lib/pleroma/emails/user_email.ex b/lib/pleroma/emails/user_email.ex index e38c681bae..cd06ab23c8 100644 --- a/lib/pleroma/emails/user_email.ex +++ b/lib/pleroma/emails/user_email.ex @@ -5,9 +5,12 @@ defmodule Pleroma.Emails.UserEmail do @moduledoc "User emails" + require Pleroma.Web.Gettext + alias Pleroma.Config alias Pleroma.User alias Pleroma.Web.Endpoint + alias Pleroma.Web.Gettext alias Pleroma.Web.Router import Swoosh.Email @@ -30,25 +33,64 @@ def welcome(user, opts \\ %{}) do new() |> to(recipient(user)) |> from(Map.get(opts, :sender, sender())) - |> subject(Map.get(opts, :subject, "Welcome to #{instance_name()}!")) - |> html_body(Map.get(opts, :html, "Welcome to #{instance_name()}!")) - |> text_body(Map.get(opts, :text, "Welcome to #{instance_name()}!")) + |> subject( + Map.get( + opts, + :subject, + Gettext.dpgettext("static_pages", "welcome email subject", "Welcome to %{instance_name}!", + instance_name: instance_name() + ) + ) + ) + |> html_body( + Map.get( + opts, + :html, + Gettext.dpgettext( + "static_pages", + "welcome email html body", + "Welcome to %{instance_name}!", + instance_name: instance_name() + ) + ) + ) + |> text_body( + Map.get( + opts, + :text, + Gettext.dpgettext( + "static_pages", + "welcome email text body", + "Welcome to %{instance_name}!", + instance_name: instance_name() + ) + ) + ) end def password_reset_email(user, token) when is_binary(token) do password_reset_url = Router.Helpers.reset_password_url(Endpoint, :reset, token) - html_body = """ -

Reset your password at #{instance_name()}

-

Someone has requested password change for your account at #{instance_name()}.

-

If it was you, visit the following link to proceed: reset password.

-

If it was someone else, nothing to worry about: your data is secure and your password has not been changed.

- """ + html_body = + Gettext.dpgettext( + "static_pages", + "password reset email body", + """ +

Reset your password at %{instance_name}

+

Someone has requested password change for your account at %{instance_name}.

+

If it was you, visit the following link to proceed: reset password.

+

If it was someone else, nothing to worry about: your data is secure and your password has not been changed.

+ """, + instance_name: instance_name(), + password_reset_url: password_reset_url + ) new() |> to(recipient(user)) |> from(sender()) - |> subject("Password reset") + |> subject( + Gettext.dpgettext("static_pages", "password reset email subject", "Password reset") + ) |> html_body(html_body) end @@ -65,16 +107,31 @@ def user_invitation_email( user_invite_token.token ) - html_body = """ -

You are invited to #{instance_name()}

-

#{user.name} invites you to join #{instance_name()}, an instance of Pleroma federated social networking platform.

-

Click the following link to register: accept invitation.

- """ + html_body = + Gettext.dpgettext( + "static_pages", + "user invitation email body", + """ +

You are invited to %{instance_name}

+

%{inviter_name} invites you to join %{instance_name}, an instance of Pleroma federated social networking platform.

+

Click the following link to register: accept invitation.

+ """, + instance_name: instance_name(), + inviter_name: user.name, + registration_url: registration_url + ) new() |> to(recipient(to_email, to_name)) |> from(sender()) - |> subject("Invitation to #{instance_name()}") + |> subject( + Gettext.dpgettext( + "static_pages", + "user invitation email subject", + "Invitation to %{instance_name}", + instance_name: instance_name() + ) + ) |> html_body(html_body) end @@ -87,43 +144,83 @@ def account_confirmation_email(user) do to_string(user.confirmation_token) ) - html_body = """ -

Thank you for registering on #{instance_name()}

-

Email confirmation is required to activate the account.

-

Please click the following link to activate your account.

- """ + html_body = + Gettext.dpgettext( + "static_pages", + "confirmation email body", + """ +

Thank you for registering on %{instance_name}

+

Email confirmation is required to activate the account.

+

Please click the following link to activate your account.

+ """, + instance_name: instance_name(), + confirmation_url: confirmation_url + ) new() |> to(recipient(user)) |> from(sender()) - |> subject("#{instance_name()} account confirmation") + |> subject( + Gettext.dpgettext( + "static_pages", + "confirmation email subject", + "%{instance_name} account confirmation", + instance_name: instance_name() + ) + ) |> html_body(html_body) end def approval_pending_email(user) do - html_body = """ -

Awaiting Approval

-

Your account at #{instance_name()} is being reviewed by staff. You will receive another email once your account is approved.

- """ + html_body = + Gettext.dpgettext( + "static_pages", + "approval pending email body", + """ +

Awaiting Approval

+

Your account at %{instance_name} is being reviewed by staff. You will receive another email once your account is approved.

+ """, + instance_name: instance_name() + ) new() |> to(recipient(user)) |> from(sender()) - |> subject("Your account is awaiting approval") + |> subject( + Gettext.dpgettext( + "static_pages", + "approval pending email subject", + "Your account is awaiting approval" + ) + ) |> html_body(html_body) end def successful_registration_email(user) do - html_body = """ -

Hello @#{user.nickname},

-

Your account at #{instance_name()} has been registered successfully.

-

No further action is required to activate your account.

- """ + html_body = + Gettext.dpgettext( + "static_pages", + "successful registration email body", + """ +

Hello @%{nickname},

+

Your account at %{instance_name} has been registered successfully.

+

No further action is required to activate your account.

+ """, + nickname: user.nickname, + instance_name: instance_name() + ) new() |> to(recipient(user)) |> from(sender()) - |> subject("Account registered on #{instance_name()}") + |> subject( + Gettext.dpgettext( + "static_pages", + "successful registration email subject", + "Account registered on %{instance_name}", + instance_name: instance_name() + ) + ) |> html_body(html_body) end @@ -193,7 +290,14 @@ def digest_email(user) do new() |> to(recipient(user)) |> from(sender()) - |> subject("Your digest from #{instance_name()}") + |> subject( + Gettext.dpgettext( + "static_pages", + "digest email subject", + "Your digest from %{instance_name}", + instance_name: instance_name() + ) + ) |> put_layout(false) |> render_body("digest.html", html_data) |> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.svg", type: :inline)) @@ -230,23 +334,40 @@ def backup_is_ready_email(backup, admin_user_id \\ nil) do html_body = if is_nil(admin_user_id) do - """ -

You requested a full backup of your Pleroma account. It's ready for download:

-

#{download_url}

- """ + Gettext.dpgettext( + "static_pages", + "account archive email body - self-requested", + """ +

You requested a full backup of your Pleroma account. It's ready for download:

+

%{download_url}

+ """, + download_url: download_url + ) else admin = Pleroma.Repo.get(User, admin_user_id) - """ -

Admin @#{admin.nickname} requested a full backup of your Pleroma account. It's ready for download:

-

#{download_url}

- """ + Gettext.dpgettext( + "static_pages", + "account archive email body - admin requested", + """ +

Admin @%{admin_nickname} requested a full backup of your Pleroma account. It's ready for download:

+

%{download_url}

+ """, + admin_nickname: admin.nickname, + download_url: download_url + ) end new() |> to(recipient(user)) |> from(sender()) - |> subject("Your account archive is ready") + |> subject( + Gettext.dpgettext( + "static_pages", + "account archive email subject", + "Your account archive is ready" + ) + ) |> html_body(html_body) end end diff --git a/priv/gettext/static_pages.pot b/priv/gettext/static_pages.pot index b76641e283..a14cedae90 100644 --- a/priv/gettext/static_pages.pot +++ b/priv/gettext/static_pages.pot @@ -409,3 +409,105 @@ msgid "%{count} New Follower" msgid_plural "%{count} New Followers" msgstr[0] "" msgstr[1] "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:349 +msgctxt "account archive email body - admin requested" +msgid "

Admin @%{admin.nickname} requested a full backup of your Pleroma account. It's ready for download:

\n

%{download_url}

\n" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:337 +msgctxt "account archive email body - self-requested" +msgid "

You requested a full backup of your Pleroma account. It's ready for download:

\n

%{download_url}

\n" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:365 +msgctxt "account archive email subject" +msgid "Your account archive is ready" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:176 +msgctxt "approval pending email body" +msgid "

Awaiting Approval

\n

Your account at %{instance_name} is being reviewed by staff. You will receive another email once your account is approved.

\n" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:190 +msgctxt "approval pending email subject" +msgid "Your account is awaiting approval" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:148 +msgctxt "confirmation email body" +msgid "

Thank you for registering on %{instance_name}

\n

Email confirmation is required to activate the account.

\n

Please click the following link to activate your account.

\n" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:164 +msgctxt "confirmation email subject" +msgid "%{instance_name} account confirmation" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:294 +msgctxt "digest email subject" +msgid "Your digest from %{instance_name}" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:75 +msgctxt "password reset email body" +msgid "

Reset your password at %{instance_name}

\n

Someone has requested password change for your account at %{instance_name}.

\n

If it was you, visit the following link to proceed: reset password.

\n

If it was someone else, nothing to worry about: your data is secure and your password has not been changed.

\n" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:92 +msgctxt "password reset email subject" +msgid "Password reset" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:201 +msgctxt "successful registration email body" +msgid "

Hello @%{nickname},

\n

Your account at %{instance_name} has been registered successfully.

\n

No further action is required to activate your account.

\n" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:217 +msgctxt "successful registration email subject" +msgid "Account registered on %{instance_name}" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:111 +msgctxt "user invitation email body" +msgid "

You are invited to %{instance_name}

\n

%{inviter_name} invites you to join %{instance_name}, an instance of Pleroma federated social networking platform.

\n

Click the following link to register: accept invitation.

\n" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:128 +msgctxt "user invitation email subject" +msgid "Invitation to %{instance_name}" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:49 +msgctxt "welcome email html body" +msgid "Welcome to %{instance_name}!" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:40 +msgctxt "welcome email subject" +msgid "Welcome to %{instance_name}!" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:61 +msgctxt "welcome email text body" +msgid "Welcome to %{instance_name}!" +msgstr "" From 0149ea453868b701d949a5cfee429dfd9d78bb65 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 1 Mar 2022 21:24:17 -0500 Subject: [PATCH 16/37] Send emails i18n'd using backend-stored user language --- lib/pleroma/emails/user_email.ex | 516 ++++++++-------- lib/pleroma/user.ex | 1 + lib/pleroma/web/gettext.ex | 22 + lib/pleroma/web/plugs/set_locale_plug.ex | 4 +- priv/gettext/en_test/LC_MESSAGES/default.po | 186 ++++++ priv/gettext/en_test/LC_MESSAGES/errors.po | 557 ++++++++++++++++++ .../en_test/LC_MESSAGES/posix_errors.po | 153 +++++ .../en_test/LC_MESSAGES/static_pages.po | 529 +++++++++++++++++ priv/gettext/static_pages.pot | 44 +- .../20220302013920_add_language_to_users.exs | 9 + test/pleroma/emails/user_email_test.exs | 12 + 11 files changed, 1760 insertions(+), 273 deletions(-) create mode 100644 priv/gettext/en_test/LC_MESSAGES/default.po create mode 100644 priv/gettext/en_test/LC_MESSAGES/errors.po create mode 100644 priv/gettext/en_test/LC_MESSAGES/posix_errors.po create mode 100644 priv/gettext/en_test/LC_MESSAGES/static_pages.po create mode 100644 priv/repo/migrations/20220302013920_add_language_to_users.exs diff --git a/lib/pleroma/emails/user_email.ex b/lib/pleroma/emails/user_email.ex index cd06ab23c8..24adfabd71 100644 --- a/lib/pleroma/emails/user_email.ex +++ b/lib/pleroma/emails/user_email.ex @@ -30,68 +30,75 @@ defp recipient(%User{} = user), do: recipient(user.email, user.name) @spec welcome(User.t(), map()) :: Swoosh.Email.t() def welcome(user, opts \\ %{}) do - new() - |> to(recipient(user)) - |> from(Map.get(opts, :sender, sender())) - |> subject( - Map.get( - opts, - :subject, - Gettext.dpgettext("static_pages", "welcome email subject", "Welcome to %{instance_name}!", - instance_name: instance_name() + Gettext.with_locale_or_default user.language do + new() + |> to(recipient(user)) + |> from(Map.get(opts, :sender, sender())) + |> subject( + Map.get( + opts, + :subject, + Gettext.dpgettext( + "static_pages", + "welcome email subject", + "Welcome to %{instance_name}!", + instance_name: instance_name() + ) ) ) - ) - |> html_body( - Map.get( - opts, - :html, - Gettext.dpgettext( - "static_pages", - "welcome email html body", - "Welcome to %{instance_name}!", - instance_name: instance_name() + |> html_body( + Map.get( + opts, + :html, + Gettext.dpgettext( + "static_pages", + "welcome email html body", + "Welcome to %{instance_name}!", + instance_name: instance_name() + ) ) ) - ) - |> text_body( - Map.get( - opts, - :text, - Gettext.dpgettext( - "static_pages", - "welcome email text body", - "Welcome to %{instance_name}!", - instance_name: instance_name() + |> text_body( + Map.get( + opts, + :text, + Gettext.dpgettext( + "static_pages", + "welcome email text body", + "Welcome to %{instance_name}!", + instance_name: instance_name() + ) ) ) - ) + end end def password_reset_email(user, token) when is_binary(token) do - password_reset_url = Router.Helpers.reset_password_url(Endpoint, :reset, token) + Gettext.with_locale_or_default user.language do + password_reset_url = Router.Helpers.reset_password_url(Endpoint, :reset, token) - html_body = - Gettext.dpgettext( - "static_pages", - "password reset email body", - """ -

Reset your password at %{instance_name}

-

Someone has requested password change for your account at %{instance_name}.

-

If it was you, visit the following link to proceed: reset password.

-

If it was someone else, nothing to worry about: your data is secure and your password has not been changed.

- """, - instance_name: instance_name(), - password_reset_url: password_reset_url + html_body = + Gettext.dpgettext( + "static_pages", + "password reset email body", + """ +

Reset your password at %{instance_name}

+

Someone has requested password change for your account at %{instance_name}.

+

If it was you, visit the following link to proceed: reset password.

+

If it was someone else, nothing to worry about: your data is secure and your password has not been changed.

+ """, + instance_name: instance_name(), + password_reset_url: password_reset_url + ) + + new() + |> to(recipient(user)) + |> from(sender()) + |> subject( + Gettext.dpgettext("static_pages", "password reset email subject", "Password reset") ) - - new() - |> to(recipient(user)) - |> from(sender()) - |> subject( - Gettext.dpgettext("static_pages", "password reset email subject", "Password reset") - ) - |> html_body(html_body) + |> html_body(html_body) + end end def user_invitation_email( @@ -100,128 +107,136 @@ def user_invitation_email( to_email, to_name \\ nil ) do - registration_url = - Router.Helpers.redirect_url( - Endpoint, - :registration_page, - user_invite_token.token - ) + Gettext.with_locale_or_default user.language do + registration_url = + Router.Helpers.redirect_url( + Endpoint, + :registration_page, + user_invite_token.token + ) - html_body = - Gettext.dpgettext( - "static_pages", - "user invitation email body", - """ -

You are invited to %{instance_name}

-

%{inviter_name} invites you to join %{instance_name}, an instance of Pleroma federated social networking platform.

-

Click the following link to register: accept invitation.

- """, - instance_name: instance_name(), - inviter_name: user.name, - registration_url: registration_url - ) + html_body = + Gettext.dpgettext( + "static_pages", + "user invitation email body", + """ +

You are invited to %{instance_name}

+

%{inviter_name} invites you to join %{instance_name}, an instance of Pleroma federated social networking platform.

+

Click the following link to register: accept invitation.

+ """, + instance_name: instance_name(), + inviter_name: user.name, + registration_url: registration_url + ) - new() - |> to(recipient(to_email, to_name)) - |> from(sender()) - |> subject( - Gettext.dpgettext( - "static_pages", - "user invitation email subject", - "Invitation to %{instance_name}", - instance_name: instance_name() + new() + |> to(recipient(to_email, to_name)) + |> from(sender()) + |> subject( + Gettext.dpgettext( + "static_pages", + "user invitation email subject", + "Invitation to %{instance_name}", + instance_name: instance_name() + ) ) - ) - |> html_body(html_body) + |> html_body(html_body) + end end def account_confirmation_email(user) do - confirmation_url = - Router.Helpers.confirm_email_url( - Endpoint, - :confirm_email, - user.id, - to_string(user.confirmation_token) - ) + Gettext.with_locale_or_default user.language do + confirmation_url = + Router.Helpers.confirm_email_url( + Endpoint, + :confirm_email, + user.id, + to_string(user.confirmation_token) + ) - html_body = - Gettext.dpgettext( - "static_pages", - "confirmation email body", - """ -

Thank you for registering on %{instance_name}

-

Email confirmation is required to activate the account.

-

Please click the following link to activate your account.

- """, - instance_name: instance_name(), - confirmation_url: confirmation_url - ) + html_body = + Gettext.dpgettext( + "static_pages", + "confirmation email body", + """ +

Thank you for registering on %{instance_name}

+

Email confirmation is required to activate the account.

+

Please click the following link to activate your account.

+ """, + instance_name: instance_name(), + confirmation_url: confirmation_url + ) - new() - |> to(recipient(user)) - |> from(sender()) - |> subject( - Gettext.dpgettext( - "static_pages", - "confirmation email subject", - "%{instance_name} account confirmation", - instance_name: instance_name() + new() + |> to(recipient(user)) + |> from(sender()) + |> subject( + Gettext.dpgettext( + "static_pages", + "confirmation email subject", + "%{instance_name} account confirmation", + instance_name: instance_name() + ) ) - ) - |> html_body(html_body) + |> html_body(html_body) + end end def approval_pending_email(user) do - html_body = - Gettext.dpgettext( - "static_pages", - "approval pending email body", - """ -

Awaiting Approval

-

Your account at %{instance_name} is being reviewed by staff. You will receive another email once your account is approved.

- """, - instance_name: instance_name() - ) + Gettext.with_locale_or_default user.language do + html_body = + Gettext.dpgettext( + "static_pages", + "approval pending email body", + """ +

Awaiting Approval

+

Your account at %{instance_name} is being reviewed by staff. You will receive another email once your account is approved.

+ """, + instance_name: instance_name() + ) - new() - |> to(recipient(user)) - |> from(sender()) - |> subject( - Gettext.dpgettext( - "static_pages", - "approval pending email subject", - "Your account is awaiting approval" + new() + |> to(recipient(user)) + |> from(sender()) + |> subject( + Gettext.dpgettext( + "static_pages", + "approval pending email subject", + "Your account is awaiting approval" + ) ) - ) - |> html_body(html_body) + |> html_body(html_body) + end end def successful_registration_email(user) do - html_body = - Gettext.dpgettext( - "static_pages", - "successful registration email body", - """ -

Hello @%{nickname},

-

Your account at %{instance_name} has been registered successfully.

-

No further action is required to activate your account.

- """, - nickname: user.nickname, - instance_name: instance_name() - ) + Gettext.with_locale_or_default user.language do + html_body = + Gettext.dpgettext( + "static_pages", + "successful registration email body", + """ +

Hello @%{nickname},

+

Your account at %{instance_name} has been registered successfully.

+

No further action is required to activate your account.

+ """, + nickname: user.nickname, + instance_name: instance_name() + ) - new() - |> to(recipient(user)) - |> from(sender()) - |> subject( - Gettext.dpgettext( - "static_pages", - "successful registration email subject", - "Account registered on %{instance_name}", - instance_name: instance_name() + new() + |> to(recipient(user)) + |> from(sender()) + |> subject( + Gettext.dpgettext( + "static_pages", + "successful registration email subject", + "Account registered on %{instance_name}", + instance_name: instance_name() + ) ) - ) - |> html_body(html_body) + |> html_body(html_body) + end end @doc """ @@ -231,76 +246,78 @@ def successful_registration_email(user) do """ @spec digest_email(User.t()) :: Swoosh.Email.t() | nil def digest_email(user) do - notifications = Pleroma.Notification.for_user_since(user, user.last_digest_emailed_at) + Gettext.with_locale_or_default user.language do + notifications = Pleroma.Notification.for_user_since(user, user.last_digest_emailed_at) - mentions = - notifications - |> Enum.filter(&(&1.activity.data["type"] == "Create")) - |> Enum.map(fn notification -> - object = Pleroma.Object.normalize(notification.activity, fetch: false) + mentions = + notifications + |> Enum.filter(&(&1.activity.data["type"] == "Create")) + |> Enum.map(fn notification -> + object = Pleroma.Object.normalize(notification.activity, fetch: false) - if not is_nil(object) do - object = update_in(object.data["content"], &format_links/1) + if not is_nil(object) do + object = update_in(object.data["content"], &format_links/1) - %{ - data: notification, - object: object, - from: User.get_by_ap_id(notification.activity.actor) - } - end - end) - |> Enum.filter(& &1) + %{ + data: notification, + object: object, + from: User.get_by_ap_id(notification.activity.actor) + } + end + end) + |> Enum.filter(& &1) - followers = - notifications - |> Enum.filter(&(&1.activity.data["type"] == "Follow")) - |> Enum.map(fn notification -> - from = User.get_by_ap_id(notification.activity.actor) + followers = + notifications + |> Enum.filter(&(&1.activity.data["type"] == "Follow")) + |> Enum.map(fn notification -> + from = User.get_by_ap_id(notification.activity.actor) - if not is_nil(from) do - %{ - data: notification, - object: Pleroma.Object.normalize(notification.activity, fetch: false), - from: User.get_by_ap_id(notification.activity.actor) - } - end - end) - |> Enum.filter(& &1) + if not is_nil(from) do + %{ + data: notification, + object: Pleroma.Object.normalize(notification.activity, fetch: false), + from: User.get_by_ap_id(notification.activity.actor) + } + end + end) + |> Enum.filter(& &1) - unless Enum.empty?(mentions) do - styling = Config.get([__MODULE__, :styling]) - logo = Config.get([__MODULE__, :logo]) + unless Enum.empty?(mentions) do + styling = Config.get([__MODULE__, :styling]) + logo = Config.get([__MODULE__, :logo]) - html_data = %{ - instance: instance_name(), - user: user, - mentions: mentions, - followers: followers, - unsubscribe_link: unsubscribe_url(user, "digest"), - styling: styling - } + html_data = %{ + instance: instance_name(), + user: user, + mentions: mentions, + followers: followers, + unsubscribe_link: unsubscribe_url(user, "digest"), + styling: styling + } - logo_path = - if is_nil(logo) do - Path.join(:code.priv_dir(:pleroma), "static/static/logo.svg") - else - Path.join(Config.get([:instance, :static_dir]), logo) - end + logo_path = + if is_nil(logo) do + Path.join(:code.priv_dir(:pleroma), "static/static/logo.svg") + else + Path.join(Config.get([:instance, :static_dir]), logo) + end - new() - |> to(recipient(user)) - |> from(sender()) - |> subject( - Gettext.dpgettext( - "static_pages", - "digest email subject", - "Your digest from %{instance_name}", - instance_name: instance_name() + new() + |> to(recipient(user)) + |> from(sender()) + |> subject( + Gettext.dpgettext( + "static_pages", + "digest email subject", + "Your digest from %{instance_name}", + instance_name: instance_name() + ) ) - ) - |> put_layout(false) - |> render_body("digest.html", html_data) - |> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.svg", type: :inline)) + |> put_layout(false) + |> render_body("digest.html", html_data) + |> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.svg", type: :inline)) + end end end @@ -330,44 +347,47 @@ def unsubscribe_url(user, notifications_type) do def backup_is_ready_email(backup, admin_user_id \\ nil) do %{user: user} = Pleroma.Repo.preload(backup, :user) - download_url = Pleroma.Web.PleromaAPI.BackupView.download_url(backup) - html_body = - if is_nil(admin_user_id) do + Gettext.with_locale_or_default user.language do + download_url = Pleroma.Web.PleromaAPI.BackupView.download_url(backup) + + html_body = + if is_nil(admin_user_id) do + Gettext.dpgettext( + "static_pages", + "account archive email body - self-requested", + """ +

You requested a full backup of your Pleroma account. It's ready for download:

+

%{download_url}

+ """, + download_url: download_url + ) + else + admin = Pleroma.Repo.get(User, admin_user_id) + + Gettext.dpgettext( + "static_pages", + "account archive email body - admin requested", + """ +

Admin @%{admin_nickname} requested a full backup of your Pleroma account. It's ready for download:

+

%{download_url}

+ """, + admin_nickname: admin.nickname, + download_url: download_url + ) + end + + new() + |> to(recipient(user)) + |> from(sender()) + |> subject( Gettext.dpgettext( "static_pages", - "account archive email body - self-requested", - """ -

You requested a full backup of your Pleroma account. It's ready for download:

-

%{download_url}

- """, - download_url: download_url + "account archive email subject", + "Your account archive is ready" ) - else - admin = Pleroma.Repo.get(User, admin_user_id) - - Gettext.dpgettext( - "static_pages", - "account archive email body - admin requested", - """ -

Admin @%{admin_nickname} requested a full backup of your Pleroma account. It's ready for download:

-

%{download_url}

- """, - admin_nickname: admin.nickname, - download_url: download_url - ) - end - - new() - |> to(recipient(user)) - |> from(sender()) - |> subject( - Gettext.dpgettext( - "static_pages", - "account archive email subject", - "Your account archive is ready" ) - ) - |> html_body(html_body) + |> html_body(html_body) + end end end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 36177bda3d..cc8a26b482 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -156,6 +156,7 @@ defmodule Pleroma.User do field(:last_status_at, :naive_datetime) field(:birthday, :date) field(:show_birthday, :boolean, default: false) + field(:language, :string) embeds_one( :notification_settings, diff --git a/lib/pleroma/web/gettext.ex b/lib/pleroma/web/gettext.ex index c8a739c2bd..e85290496e 100644 --- a/lib/pleroma/web/gettext.ex +++ b/lib/pleroma/web/gettext.ex @@ -34,4 +34,26 @@ def language_tag do Gettext.get_locale() |> String.replace("_", "-", global: true) end + + def supports_locale?(locale) do + Pleroma.Web.Gettext + |> Gettext.known_locales() + |> Enum.member?(locale) + end + + def locale_or_default(locale) do + if supports_locale?(locale) do + locale + else + Gettext.get_locale() + end + end + + defmacro with_locale_or_default(locale, do: fun) do + quote do + Gettext.with_locale(Pleroma.Web.Gettext.locale_or_default(unquote(locale)), fn -> + unquote(fun) + end) + end + end end diff --git a/lib/pleroma/web/plugs/set_locale_plug.ex b/lib/pleroma/web/plugs/set_locale_plug.ex index a9387ba7e1..3c3fffa81a 100644 --- a/lib/pleroma/web/plugs/set_locale_plug.ex +++ b/lib/pleroma/web/plugs/set_locale_plug.ex @@ -64,9 +64,7 @@ defp extract_accept_language(conn) do end defp supported_locale?(locale) do - Pleroma.Web.Gettext - |> Gettext.known_locales() - |> Enum.member?(locale) + Pleroma.Web.Gettext.supports_locale?(locale) end defp parse_language_option(string) do diff --git a/priv/gettext/en_test/LC_MESSAGES/default.po b/priv/gettext/en_test/LC_MESSAGES/default.po new file mode 100644 index 0000000000..63db74608a --- /dev/null +++ b/priv/gettext/en_test/LC_MESSAGES/default.po @@ -0,0 +1,186 @@ +## "msgid"s in this file come from POT (.pot) files. +## +## Do not add, change, or remove "msgid"s manually here as +## they're tied to the ones in the corresponding POT file +## (with the same domain). +## +## Use "mix gettext.extract --merge" or "mix gettext.merge" +## to merge POT files into PO files. +msgid "" +msgstr "" +"Language: en_test\n" +"Plural-Forms: nplurals=2\n" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:122 +msgid "%{name} - %{count} is not a multiple of %{multiple}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:131 +msgid "%{name} - %{value} is larger than exclusive maximum %{max}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:140 +msgid "%{name} - %{value} is larger than inclusive maximum %{max}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:149 +msgid "%{name} - %{value} is smaller than exclusive minimum %{min}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:158 +msgid "%{name} - %{value} is smaller than inclusive minimum %{min}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:102 +msgid "%{name} - Array items must be unique." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:114 +msgid "%{name} - Array length %{length} is larger than maxItems: %{}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:106 +msgid "%{name} - Array length %{length} is smaller than minItems: %{min}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:166 +msgid "%{name} - Invalid %{type}. Got: %{value}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:174 +msgid "%{name} - Invalid format. Expected %{format}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:51 +msgid "%{name} - Invalid schema.type. Got: %{type}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:178 +msgid "%{name} - Invalid value for enum." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:95 +msgid "%{name} - String length is larger than maxLength: %{length}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:88 +msgid "%{name} - String length is smaller than minLength: %{length}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:63 +msgid "%{name} - null value where %{type} expected." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:60 +msgid "%{name} - null value." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:182 +msgid "Failed to cast to any schema in %{polymorphic_type}" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:71 +msgid "Failed to cast value as %{invalid_schema}. Value must be castable using `allOf` schemas listed." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:84 +msgid "Failed to cast value to one of: %{failed_schemas}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:78 +msgid "Failed to cast value using any of: %{failed_schemas}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:212 +msgid "Invalid value for header: %{name}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:204 +msgid "Missing field: %{name}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:208 +msgid "Missing header: %{name}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:196 +msgid "No value provided for required discriminator `%{field}`." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:216 +msgid "Object property count %{property_count} is greater than maxProperties: %{max_properties}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:224 +msgid "Object property count %{property_count} is less than minProperties: %{min_properties}" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/static_fe/static_fe/error.html.eex:2 +msgid "Oops" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:188 +msgid "Unexpected field: %{name}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:200 +msgid "Unknown schema: %{name}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/api_spec/render_error.ex:192 +msgid "Value used as discriminator for `%{field}` matches no schemas." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/embed/show.html.eex:43 +#: lib/pleroma/web/templates/static_fe/static_fe/_notice.html.eex:37 +msgid "announces" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/embed/show.html.eex:44 +#: lib/pleroma/web/templates/static_fe/static_fe/_notice.html.eex:38 +msgid "likes" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/embed/show.html.eex:42 +#: lib/pleroma/web/templates/static_fe/static_fe/_notice.html.eex:36 +msgid "replies" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/embed/show.html.eex:27 +#: lib/pleroma/web/templates/static_fe/static_fe/_notice.html.eex:22 +msgid "sensitive media" +msgstr "" diff --git a/priv/gettext/en_test/LC_MESSAGES/errors.po b/priv/gettext/en_test/LC_MESSAGES/errors.po new file mode 100644 index 0000000000..a40de7f8ba --- /dev/null +++ b/priv/gettext/en_test/LC_MESSAGES/errors.po @@ -0,0 +1,557 @@ +## "msgid"s in this file come from POT (.pot) files. +## +## Do not add, change, or remove "msgid"s manually here as +## they're tied to the ones in the corresponding POT file +## (with the same domain). +## +## Use "mix gettext.extract --merge" or "mix gettext.merge" +## to merge POT files into PO files. +msgid "" +msgstr "" +"Language: en_test\n" +"Plural-Forms: nplurals=2\n" + +msgid "can't be blank" +msgstr "" + +msgid "has already been taken" +msgstr "" + +msgid "is invalid" +msgstr "" + +msgid "has invalid format" +msgstr "" + +msgid "has an invalid entry" +msgstr "" + +msgid "is reserved" +msgstr "" + +msgid "does not match confirmation" +msgstr "" + +msgid "is still associated with this entry" +msgstr "" + +msgid "are still associated with this entry" +msgstr "" + +msgid "should be %{count} character(s)" +msgid_plural "should be %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have %{count} item(s)" +msgid_plural "should have %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at least %{count} character(s)" +msgid_plural "should be at least %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at least %{count} item(s)" +msgid_plural "should have at least %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at most %{count} character(s)" +msgid_plural "should be at most %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at most %{count} item(s)" +msgid_plural "should have at most %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "must be less than %{number}" +msgstr "" + +msgid "must be greater than %{number}" +msgstr "" + +msgid "must be less than or equal to %{number}" +msgstr "" + +msgid "must be greater than or equal to %{number}" +msgstr "" + +msgid "must be equal to %{number}" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/common_api.ex:523 +msgid "Account not found" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/common_api.ex:316 +msgid "Already voted" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/o_auth/o_auth_controller.ex:402 +msgid "Bad request" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/controller_helper.ex:97 +#: lib/pleroma/web/controller_helper.ex:103 +msgid "Can't display this activity" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:324 +msgid "Can't find user" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:80 +msgid "Can't get favorites" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/common_api/utils.ex:482 +msgid "Cannot post an empty status without attachments" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/common_api/utils.ex:441 +msgid "Comment must be up to %{max_size} characters" +msgstr "" + +#, elixir-format +#: lib/pleroma/config_db.ex:200 +msgid "Config with params %{params} not found" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/common_api.ex:167 lib/pleroma/web/common_api.ex:171 +msgid "Could not delete" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/common_api.ex:217 +msgid "Could not favorite" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/common_api.ex:254 +msgid "Could not unfavorite" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/common_api.ex:202 +msgid "Could not unrepeat" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/common_api.ex:530 lib/pleroma/web/common_api.ex:539 +msgid "Could not update state" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:205 +msgid "Error." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/twitter_api/twitter_api.ex:99 +msgid "Invalid CAPTCHA" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:144 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:631 +msgid "Invalid credentials" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/plugs/ensure_authenticated_plug.ex:42 +msgid "Invalid credentials." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/common_api.ex:337 +msgid "Invalid indices" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:29 +msgid "Invalid parameters" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/common_api/utils.ex:349 +msgid "Invalid password." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:254 +msgid "Invalid request" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/twitter_api/twitter_api.ex:102 +msgid "Kocaptcha service unavailable" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:140 +msgid "Missing parameters" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/common_api/utils.ex:477 +msgid "No such conversation" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:171 +#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:197 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:239 +msgid "No such permission_group" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:504 +#: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:11 lib/pleroma/web/feed/tag_controller.ex:16 +#: lib/pleroma/web/feed/user_controller.ex:69 lib/pleroma/web/o_status/o_status_controller.ex:132 +#: lib/pleroma/web/plugs/uploaded_media.ex:84 +msgid "Not found" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/common_api.ex:308 +msgid "Poll's author can't vote" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:20 +#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:39 lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:51 +#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:52 lib/pleroma/web/mastodon_api/controllers/status_controller.ex:326 +#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:71 +msgid "Record not found" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:35 +#: lib/pleroma/web/feed/user_controller.ex:78 lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:42 +#: lib/pleroma/web/o_status/o_status_controller.ex:138 +msgid "Something went wrong" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/common_api/activity_draft.ex:143 +msgid "The message visibility must be direct" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/common_api/utils.ex:492 +msgid "The status is over the character limit" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/plugs/ensure_public_or_authenticated_plug.ex:36 +msgid "This resource requires authentication." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/plugs/rate_limiter.ex:208 +msgid "Throttled" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/common_api.ex:338 +msgid "Too many choices" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:268 +msgid "You can't revoke your own admin status." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/o_auth/o_auth_controller.ex:243 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:333 +msgid "Your account is currently disabled" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/o_auth/o_auth_controller.ex:205 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:356 +msgid "Your login is missing a confirmed e-mail address" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:392 +msgid "can't read inbox of %{nickname} as %{as_nickname}" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:491 +msgid "can't update outbox of %{nickname} as %{as_nickname}" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/common_api.ex:475 +msgid "conversation is already muted" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:510 +msgid "error" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:34 +msgid "mascots can only be images" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:63 +msgid "not found" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/o_auth/o_auth_controller.ex:437 +msgid "Bad OAuth request." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/twitter_api/twitter_api.ex:108 +msgid "CAPTCHA already used" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/twitter_api/twitter_api.ex:105 +msgid "CAPTCHA expired" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/plugs/uploaded_media.ex:57 +msgid "Failed" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/o_auth/o_auth_controller.ex:453 +msgid "Failed to authenticate: %{message}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/o_auth/o_auth_controller.ex:484 +msgid "Failed to set up user account." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/plugs/o_auth_scopes_plug.ex:37 +msgid "Insufficient permissions: %{permissions}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/plugs/uploaded_media.ex:111 +msgid "Internal Error" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/o_auth/fallback_controller.ex:22 +#: lib/pleroma/web/o_auth/fallback_controller.ex:29 +msgid "Invalid Username/Password" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/twitter_api/twitter_api.ex:111 +msgid "Invalid answer data" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:33 +msgid "Nodeinfo schema version not handled" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/o_auth/o_auth_controller.ex:194 +msgid "This action is outside the authorized scopes" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/o_auth/fallback_controller.ex:14 +msgid "Unknown error, please check the details and try again." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/o_auth/o_auth_controller.ex:136 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:180 +msgid "Unlisted redirect_uri." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/o_auth/o_auth_controller.ex:433 +msgid "Unsupported OAuth provider: %{provider}." +msgstr "" + +#, elixir-format +#: lib/pleroma/uploaders/uploader.ex:74 +msgid "Uploader callback timeout" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/uploader_controller.ex:23 +msgid "bad request" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/twitter_api/twitter_api.ex:96 +msgid "CAPTCHA Error" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/common_api.ex:266 +msgid "Could not add reaction emoji" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/common_api.ex:277 +msgid "Could not remove reaction emoji" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/twitter_api/twitter_api.ex:122 +msgid "Invalid CAPTCHA (Missing parameter: %{name})" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/mastodon_api/controllers/list_controller.ex:96 +msgid "List not found" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:151 +msgid "Missing parameter: %{name}" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/o_auth/o_auth_controller.ex:232 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:346 +msgid "Password reset is required" +msgstr "" + +#, elixir-format +#: lib/pleroma/tests/auth_test_controller.ex:9 +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:6 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:6 +#: lib/pleroma/web/admin_api/controllers/chat_controller.ex:6 lib/pleroma/web/admin_api/controllers/config_controller.ex:6 +#: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:6 lib/pleroma/web/admin_api/controllers/frontend_controller.ex:6 +#: lib/pleroma/web/admin_api/controllers/instance_controller.ex:6 lib/pleroma/web/admin_api/controllers/instance_document_controller.ex:6 +#: lib/pleroma/web/admin_api/controllers/invite_controller.ex:6 lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex:6 +#: lib/pleroma/web/admin_api/controllers/o_auth_app_controller.ex:6 lib/pleroma/web/admin_api/controllers/relay_controller.ex:6 +#: lib/pleroma/web/admin_api/controllers/report_controller.ex:6 lib/pleroma/web/admin_api/controllers/status_controller.ex:6 +#: lib/pleroma/web/admin_api/controllers/user_controller.ex:6 lib/pleroma/web/controller_helper.ex:6 lib/pleroma/web/embed_controller.ex:6 +#: lib/pleroma/web/fallback/redirect_controller.ex:6 lib/pleroma/web/feed/tag_controller.ex:6 +#: lib/pleroma/web/feed/user_controller.ex:6 lib/pleroma/web/mailer/subscription_controller.ex:6 +#: lib/pleroma/web/manifest_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/account_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/app_controller.ex:11 lib/pleroma/web/mastodon_api/controllers/auth_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/directory_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/filter_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/instance_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/list_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/marker_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex:14 lib/pleroma/web/mastodon_api/controllers/media_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/notification_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/report_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/search_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/status_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:7 lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:6 lib/pleroma/web/media_proxy/media_proxy_controller.ex:6 +#: lib/pleroma/web/mongoose_im/mongoose_im_controller.ex:6 lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:6 +#: lib/pleroma/web/o_auth/fallback_controller.ex:6 lib/pleroma/web/o_auth/mfa_controller.ex:10 +#: lib/pleroma/web/o_auth/o_auth_controller.ex:6 lib/pleroma/web/o_status/o_status_controller.ex:6 +#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/app_controller.ex:6 +#: lib/pleroma/web/pleroma_api/controllers/backup_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/chat_controller.ex:5 +#: lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex:6 +#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex:6 +#: lib/pleroma/web/pleroma_api/controllers/instances_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:6 +#: lib/pleroma/web/pleroma_api/controllers/notification_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/report_controller.ex:6 +#: lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex:6 +#: lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex:7 lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex:6 +#: lib/pleroma/web/static_fe/static_fe_controller.ex:6 lib/pleroma/web/twitter_api/controller.ex:6 +#: lib/pleroma/web/twitter_api/controllers/password_controller.ex:10 lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex:6 +#: lib/pleroma/web/twitter_api/controllers/util_controller.ex:6 lib/pleroma/web/uploader_controller.ex:6 +#: lib/pleroma/web/web_finger/web_finger_controller.ex:6 +msgid "Security violation: OAuth scopes check was neither handled nor explicitly skipped." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/plugs/ensure_authenticated_plug.ex:32 +msgid "Two-factor authentication enabled, you must use a access token." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61 +msgid "Web push subscription is disabled on this Pleroma instance" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:234 +msgid "You can't revoke your own admin/moderator status." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:129 +msgid "authorization required for timeline view" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:24 +msgid "Access denied" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:321 +msgid "This API requires an authenticated user" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex:26 +#: lib/pleroma/web/plugs/user_is_admin_plug.ex:21 +msgid "User is not an admin." +msgstr "" + +#, elixir-format +#: lib/pleroma/user/backup.ex:75 +msgid "Last export was less than a day ago" +msgid_plural "Last export was less than %{days} days ago" +msgstr[0] "" +msgstr[1] "" + +#, elixir-format +#: lib/pleroma/user/backup.ex:93 +msgid "Backups require enabled email" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:423 +msgid "Character limit (%{limit} characters) exceeded, contains %{length} characters" +msgstr "" + +#, elixir-format +#: lib/pleroma/user/backup.ex:98 +msgid "Email is required" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/common_api/utils.ex:507 +msgid "Too many attachments" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex:33 +#: lib/pleroma/web/plugs/user_is_staff_plug.ex:20 +msgid "User is not a staff member." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/o_auth/o_auth_controller.ex:366 +msgid "Your account is awaiting approval." +msgstr "" diff --git a/priv/gettext/en_test/LC_MESSAGES/posix_errors.po b/priv/gettext/en_test/LC_MESSAGES/posix_errors.po new file mode 100644 index 0000000000..663fc59242 --- /dev/null +++ b/priv/gettext/en_test/LC_MESSAGES/posix_errors.po @@ -0,0 +1,153 @@ +## "msgid"s in this file come from POT (.pot) files. +## +## Do not add, change, or remove "msgid"s manually here as +## they're tied to the ones in the corresponding POT file +## (with the same domain). +## +## Use "mix gettext.extract --merge" or "mix gettext.merge" +## to merge POT files into PO files. +msgid "" +msgstr "" +"Language: en_test\n" +"Plural-Forms: nplurals=2\n" + +msgid "eperm" +msgstr "" + +msgid "eacces" +msgstr "" + +msgid "eagain" +msgstr "" + +msgid "ebadf" +msgstr "" + +msgid "ebadmsg" +msgstr "" + +msgid "ebusy" +msgstr "" + +msgid "edeadlk" +msgstr "" + +msgid "edeadlock" +msgstr "" + +msgid "edquot" +msgstr "" + +msgid "eexist" +msgstr "" + +msgid "efault" +msgstr "" + +msgid "efbig" +msgstr "" + +msgid "eftype" +msgstr "" + +msgid "eintr" +msgstr "" + +msgid "einval" +msgstr "" + +msgid "eio" +msgstr "" + +msgid "eisdir" +msgstr "" + +msgid "eloop" +msgstr "" + +msgid "emfile" +msgstr "" + +msgid "emlink" +msgstr "" + +msgid "emultihop" +msgstr "" + +msgid "enametoolong" +msgstr "" + +msgid "enfile" +msgstr "" + +msgid "enobufs" +msgstr "" + +msgid "enodev" +msgstr "" + +msgid "enolck" +msgstr "" + +msgid "enolink" +msgstr "" + +msgid "enoent" +msgstr "" + +msgid "enomem" +msgstr "" + +msgid "enospc" +msgstr "" + +msgid "enosr" +msgstr "" + +msgid "enostr" +msgstr "" + +msgid "enosys" +msgstr "" + +msgid "enotblk" +msgstr "" + +msgid "enotdir" +msgstr "" + +msgid "enotsup" +msgstr "" + +msgid "enxio" +msgstr "" + +msgid "eopnotsupp" +msgstr "" + +msgid "eoverflow" +msgstr "" + +msgid "epipe" +msgstr "" + +msgid "erange" +msgstr "" + +msgid "erofs" +msgstr "" + +msgid "espipe" +msgstr "" + +msgid "esrch" +msgstr "" + +msgid "estale" +msgstr "" + +msgid "etxtbsy" +msgstr "" + +msgid "exdev" +msgstr "" diff --git a/priv/gettext/en_test/LC_MESSAGES/static_pages.po b/priv/gettext/en_test/LC_MESSAGES/static_pages.po new file mode 100644 index 0000000000..a3378089cd --- /dev/null +++ b/priv/gettext/en_test/LC_MESSAGES/static_pages.po @@ -0,0 +1,529 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Free Software Foundation, Inc. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"PO-Revision-Date: 2022-03-01 21:15-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#~ ## "msgid"s in this file come from POT (.pot) files. +#~ ## +#~ ## Do not add, change, or remove "msgid"s manually here as +#~ ## they're tied to the ones in the corresponding POT file +#~ ## (with the same domain). +#~ ## +#~ ## Use "mix gettext.extract --merge" or "mix gettext.merge" +#~ ## to merge POT files into PO files. +#~ msgid "" +#~ msgstr "" +#~ "Language: en_test\n" +#~ "Plural-Forms: nplurals=2\n" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex:9 +msgctxt "remote follow authorization button" +msgid "Authorize" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex:2 +msgctxt "remote follow error" +msgid "Error fetching user" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex:4 +msgctxt "remote follow header" +msgid "Remote follow" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex:8 +msgctxt "placeholder text for auth code entry" +msgid "Authentication code" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:10 +msgctxt "placeholder text for password entry" +msgid "Password" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:8 +msgctxt "placeholder text for username entry" +msgid "Username" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:13 +msgctxt "remote follow authorization button for login" +msgid "Authorize" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex:12 +msgctxt "remote follow authorization button for mfa" +msgid "Authorize" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex:2 +msgctxt "remote follow error" +msgid "Error following account" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:4 +msgctxt "remote follow header, need login" +msgid "Log in to follow" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex:4 +msgctxt "remote follow mfa header" +msgid "Two-factor authentication" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex:4 +msgctxt "remote follow success" +msgid "Account followed!" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:7 +msgctxt "placeholder text for account id" +msgid "Your account ID, e.g. lain@quitter.se" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:8 +msgctxt "remote follow authorization button for following with a remote account" +msgid "Follow" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:2 +msgctxt "remote follow error" +msgid "Error: %{error}" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:4 +msgctxt "remote follow header" +msgid "Remotely follow %{nickname}" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/password/reset.html.eex:12 +msgctxt "password reset button" +msgid "Reset" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex:4 +msgctxt "password reset failed homepage link" +msgid "Homepage" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex:1 +msgctxt "password reset failed message" +msgid "Password reset failed" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/password/reset.html.eex:8 +msgctxt "password reset form confirm password prompt" +msgid "Confirmation" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/password/reset.html.eex:4 +msgctxt "password reset form password prompt" +msgid "Password" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex:1 +msgctxt "password reset invalid token message" +msgid "Invalid Token" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex:2 +msgctxt "password reset successful homepage link" +msgid "Homepage" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex:1 +msgctxt "password reset successful message" +msgid "Password changed!" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/feed/feed/tag.atom.eex:15 +#: lib/pleroma/web/templates/feed/feed/tag.rss.eex:7 +msgctxt "tag feed description" +msgid "These are public toots tagged with #%{tag}. You can interact with them if you have an account anywhere in the fediverse." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex:1 +msgctxt "oauth authorization exists page title" +msgid "Authorization exists" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:32 +msgctxt "oauth authorize approve button" +msgid "Approve" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:30 +msgctxt "oauth authorize cancel button" +msgid "Cancel" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:23 +msgctxt "oauth authorize message" +msgid "Application %{client_name} is requesting access to your account." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex:1 +msgctxt "oauth authorized page title" +msgid "Successfully authorized" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex:1 +msgctxt "oauth external provider page title" +msgid "Sign in with external provider" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex:13 +msgctxt "oauth external provider sign in button" +msgid "Sign in with %{strategy}" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:54 +msgctxt "oauth login button" +msgid "Log In" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:51 +msgctxt "oauth login password prompt" +msgid "Password" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:47 +msgctxt "oauth login username prompt" +msgid "Username" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:39 +msgctxt "oauth register nickname prompt" +msgid "Pleroma Handle" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:37 +msgctxt "oauth register nickname unchangeable warning" +msgid "Choose carefully! You won't be able to change this later. You will be able to change your display name, though." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:18 +msgctxt "oauth register page email prompt" +msgid "Email" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:10 +msgctxt "oauth register page fill form prompt" +msgid "If you'd like to register a new account, please provide the details below." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:35 +msgctxt "oauth register page login button" +msgid "Proceed as existing user" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:31 +msgctxt "oauth register page login password prompt" +msgid "Password" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:24 +msgctxt "oauth register page login prompt" +msgid "Alternatively, sign in to connect to existing account." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:27 +msgctxt "oauth register page login username prompt" +msgid "Name or email" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:14 +msgctxt "oauth register page nickname prompt" +msgid "Nickname" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:22 +msgctxt "oauth register page register button" +msgid "Proceed as new user" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:8 +msgctxt "oauth register page title" +msgid "Registration Details" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:36 +msgctxt "oauth register page title" +msgid "This is the first time you visit! Please enter your Pleroma handle." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex:2 +msgctxt "oauth scopes message" +msgid "The following permissions will be granted" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex:2 +#: lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex:2 +msgctxt "oauth token code message" +msgid "Token code is
%{token}" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:12 +msgctxt "mfa auth code prompt" +msgid "Authentication code" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:8 +msgctxt "mfa auth page title" +msgid "Two-factor authentication" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:23 +msgctxt "mfa auth page use recovery code link" +msgid "Enter a two-factor recovery code" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:20 +msgctxt "mfa auth verify code button" +msgid "Verify" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:8 +msgctxt "mfa recover page title" +msgid "Two-factor recovery" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:12 +msgctxt "mfa recover recovery code prompt" +msgid "Recovery code" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:23 +msgctxt "mfa recover use 2fa code link" +msgid "Enter a two-factor code" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:20 +msgctxt "mfa recover verify recovery code button" +msgid "Verify" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex:8 +msgctxt "static fe profile page remote follow button" +msgid "Remote follow" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/email/digest.html.eex:163 +msgctxt "digest email header line" +msgid "Hey %{nickname}, here is what you've missed!" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/email/digest.html.eex:544 +msgctxt "digest email receiver address" +msgid "The email address you are subscribed as is %{email}. " +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/email/digest.html.eex:538 +msgctxt "digest email sending reason" +msgid "You have received this email because you have signed up to receive digest emails from %{instance} Pleroma instance." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/email/digest.html.eex:547 +msgctxt "digest email unsubscribe action" +msgid "To unsubscribe, please go %{here}." +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/email/digest.html.eex:547 +msgctxt "digest email unsubscribe action link text" +msgid "here" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex:1 +msgctxt "mailer unsubscribe failed message" +msgid "UNSUBSCRIBE FAILURE" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex:1 +msgctxt "mailer unsubscribe successful message" +msgid "UNSUBSCRIBE SUCCESSFUL" +msgstr "" + +#, elixir-format +#: lib/pleroma/web/templates/email/digest.html.eex:385 +msgctxt "new followers count header" +msgid "%{count} New Follower" +msgid_plural "%{count} New Followers" +msgstr[0] "" +msgstr[1] "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:356 +msgctxt "account archive email body - self-requested" +msgid "

You requested a full backup of your Pleroma account. It's ready for download:

\n

%{download_url}

\n" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:384 +msgctxt "account archive email subject" +msgid "Your account archive is ready" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:188 +msgctxt "approval pending email body" +msgid "

Awaiting Approval

\n

Your account at %{instance_name} is being reviewed by staff. You will receive another email once your account is approved.

\n" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:202 +msgctxt "approval pending email subject" +msgid "Your account is awaiting approval" +msgstr "xxYour account is awaiting approvalxx" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:158 +msgctxt "confirmation email body" +msgid "

Thank you for registering on %{instance_name}

\n

Email confirmation is required to activate the account.

\n

Please click the following link to activate your account.

\n" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:174 +msgctxt "confirmation email subject" +msgid "%{instance_name} account confirmation" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:310 +msgctxt "digest email subject" +msgid "Your digest from %{instance_name}" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:81 +msgctxt "password reset email body" +msgid "

Reset your password at %{instance_name}

\n

Someone has requested password change for your account at %{instance_name}.

\n

If it was you, visit the following link to proceed: reset password.

\n

If it was someone else, nothing to worry about: your data is secure and your password has not been changed.

\n" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:98 +msgctxt "password reset email subject" +msgid "Password reset" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:215 +msgctxt "successful registration email body" +msgid "

Hello @%{nickname},

\n

Your account at %{instance_name} has been registered successfully.

\n

No further action is required to activate your account.

\n" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:231 +msgctxt "successful registration email subject" +msgid "Account registered on %{instance_name}" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:119 +msgctxt "user invitation email body" +msgid "

You are invited to %{instance_name}

\n

%{inviter_name} invites you to join %{instance_name}, an instance of Pleroma federated social networking platform.

\n

Click the following link to register: accept invitation.

\n" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:136 +msgctxt "user invitation email subject" +msgid "Invitation to %{instance_name}" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:53 +msgctxt "welcome email html body" +msgid "Welcome to %{instance_name}!" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:41 +msgctxt "welcome email subject" +msgid "Welcome to %{instance_name}!" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:65 +msgctxt "welcome email text body" +msgid "Welcome to %{instance_name}!" +msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:368 +msgctxt "account archive email body - admin requested" +msgid "

Admin @%{admin_nickname} requested a full backup of your Pleroma account. It's ready for download:

\n

%{download_url}

\n" +msgstr "" diff --git a/priv/gettext/static_pages.pot b/priv/gettext/static_pages.pot index a14cedae90..fbc3e61a34 100644 --- a/priv/gettext/static_pages.pot +++ b/priv/gettext/static_pages.pot @@ -411,103 +411,103 @@ msgstr[0] "" msgstr[1] "" #, elixir-format -#: lib/pleroma/emails/user_email.ex:349 -msgctxt "account archive email body - admin requested" -msgid "

Admin @%{admin.nickname} requested a full backup of your Pleroma account. It's ready for download:

\n

%{download_url}

\n" -msgstr "" - -#, elixir-format -#: lib/pleroma/emails/user_email.ex:337 +#: lib/pleroma/emails/user_email.ex:356 msgctxt "account archive email body - self-requested" msgid "

You requested a full backup of your Pleroma account. It's ready for download:

\n

%{download_url}

\n" msgstr "" #, elixir-format -#: lib/pleroma/emails/user_email.ex:365 +#: lib/pleroma/emails/user_email.ex:384 msgctxt "account archive email subject" msgid "Your account archive is ready" msgstr "" #, elixir-format -#: lib/pleroma/emails/user_email.ex:176 +#: lib/pleroma/emails/user_email.ex:188 msgctxt "approval pending email body" msgid "

Awaiting Approval

\n

Your account at %{instance_name} is being reviewed by staff. You will receive another email once your account is approved.

\n" msgstr "" #, elixir-format -#: lib/pleroma/emails/user_email.ex:190 +#: lib/pleroma/emails/user_email.ex:202 msgctxt "approval pending email subject" msgid "Your account is awaiting approval" msgstr "" #, elixir-format -#: lib/pleroma/emails/user_email.ex:148 +#: lib/pleroma/emails/user_email.ex:158 msgctxt "confirmation email body" msgid "

Thank you for registering on %{instance_name}

\n

Email confirmation is required to activate the account.

\n

Please click the following link to activate your account.

\n" msgstr "" #, elixir-format -#: lib/pleroma/emails/user_email.ex:164 +#: lib/pleroma/emails/user_email.ex:174 msgctxt "confirmation email subject" msgid "%{instance_name} account confirmation" msgstr "" #, elixir-format -#: lib/pleroma/emails/user_email.ex:294 +#: lib/pleroma/emails/user_email.ex:310 msgctxt "digest email subject" msgid "Your digest from %{instance_name}" msgstr "" #, elixir-format -#: lib/pleroma/emails/user_email.ex:75 +#: lib/pleroma/emails/user_email.ex:81 msgctxt "password reset email body" msgid "

Reset your password at %{instance_name}

\n

Someone has requested password change for your account at %{instance_name}.

\n

If it was you, visit the following link to proceed: reset password.

\n

If it was someone else, nothing to worry about: your data is secure and your password has not been changed.

\n" msgstr "" #, elixir-format -#: lib/pleroma/emails/user_email.ex:92 +#: lib/pleroma/emails/user_email.ex:98 msgctxt "password reset email subject" msgid "Password reset" msgstr "" #, elixir-format -#: lib/pleroma/emails/user_email.ex:201 +#: lib/pleroma/emails/user_email.ex:215 msgctxt "successful registration email body" msgid "

Hello @%{nickname},

\n

Your account at %{instance_name} has been registered successfully.

\n

No further action is required to activate your account.

\n" msgstr "" #, elixir-format -#: lib/pleroma/emails/user_email.ex:217 +#: lib/pleroma/emails/user_email.ex:231 msgctxt "successful registration email subject" msgid "Account registered on %{instance_name}" msgstr "" #, elixir-format -#: lib/pleroma/emails/user_email.ex:111 +#: lib/pleroma/emails/user_email.ex:119 msgctxt "user invitation email body" msgid "

You are invited to %{instance_name}

\n

%{inviter_name} invites you to join %{instance_name}, an instance of Pleroma federated social networking platform.

\n

Click the following link to register: accept invitation.

\n" msgstr "" #, elixir-format -#: lib/pleroma/emails/user_email.ex:128 +#: lib/pleroma/emails/user_email.ex:136 msgctxt "user invitation email subject" msgid "Invitation to %{instance_name}" msgstr "" #, elixir-format -#: lib/pleroma/emails/user_email.ex:49 +#: lib/pleroma/emails/user_email.ex:53 msgctxt "welcome email html body" msgid "Welcome to %{instance_name}!" msgstr "" #, elixir-format -#: lib/pleroma/emails/user_email.ex:40 +#: lib/pleroma/emails/user_email.ex:41 msgctxt "welcome email subject" msgid "Welcome to %{instance_name}!" msgstr "" #, elixir-format -#: lib/pleroma/emails/user_email.ex:61 +#: lib/pleroma/emails/user_email.ex:65 msgctxt "welcome email text body" msgid "Welcome to %{instance_name}!" msgstr "" + +#, elixir-format +#: lib/pleroma/emails/user_email.ex:368 +msgctxt "account archive email body - admin requested" +msgid "

Admin @%{admin_nickname} requested a full backup of your Pleroma account. It's ready for download:

\n

%{download_url}

\n" +msgstr "" diff --git a/priv/repo/migrations/20220302013920_add_language_to_users.exs b/priv/repo/migrations/20220302013920_add_language_to_users.exs new file mode 100644 index 0000000000..7a63c36aad --- /dev/null +++ b/priv/repo/migrations/20220302013920_add_language_to_users.exs @@ -0,0 +1,9 @@ +defmodule Pleroma.Repo.Migrations.AddLanguageToUsers do + use Ecto.Migration + + def change do + alter table(:users) do + add_if_not_exists(:language, :string) + end + end +end diff --git a/test/pleroma/emails/user_email_test.exs b/test/pleroma/emails/user_email_test.exs index 21fd06ea67..771a9a4901 100644 --- a/test/pleroma/emails/user_email_test.exs +++ b/test/pleroma/emails/user_email_test.exs @@ -56,4 +56,16 @@ test "build approval pending email" do assert email.subject == "Your account is awaiting approval" assert email.html_body =~ "Awaiting Approval" end + + test "email i18n" do + user = insert(:user, language: "en_test") + email = UserEmail.approval_pending_email(user) + assert email.subject == "xxYour account is awaiting approvalxx" + end + + test "email i18n should fallback to default locale if user language is unsupported" do + user = insert(:user, language: "unsupported") + email = UserEmail.approval_pending_email(user) + assert email.subject == "Your account is awaiting approval" + end end From 396f036b132271ebb0a6a88d28672792528b3b9c Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 2 Mar 2022 00:58:02 -0500 Subject: [PATCH 17/37] Allow update_credentials to update User.language --- lib/pleroma/web/mastodon_api/controllers/account_controller.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 8e6d49168f..2c97cadc29 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -221,6 +221,7 @@ def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _p # Note: param name is indeed :discoverable (not an error) |> Maps.put_if_present(:is_discoverable, params[:discoverable]) |> Maps.put_if_present(:birthday, params[:birthday]) + |> Maps.put_if_present(:language, params[:language]) # What happens here: # From e644f8dea52cb823076f63a18b18c1566c5190b6 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 2 Mar 2022 01:41:13 -0500 Subject: [PATCH 18/37] Allow user to register with custom language --- lib/pleroma/user.ex | 3 +- .../api_spec/operations/account_operation.ex | 5 ++ lib/pleroma/web/gettext.ex | 8 +++ .../controllers/account_controller.ex | 2 +- lib/pleroma/web/plugs/set_locale_plug.ex | 2 +- lib/pleroma/web/twitter_api/twitter_api.ex | 6 ++ .../controllers/account_controller_test.exs | 70 +++++++++++++++++++ 7 files changed, 93 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index cc8a26b482..6bce832eaf 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -747,7 +747,8 @@ def register_changeset(struct, params \\ %{}, opts \\ []) do :emoji, :accepts_chat_messages, :registration_reason, - :birthday + :birthday, + :language ]) |> validate_required([:name, :nickname, :password, :password_confirmation]) |> validate_confirmation(:password) diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 03efa3c384..c704ef5d66 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -549,6 +549,11 @@ defp create_request do nullable: true, description: "User's birthday", format: :date + }, + language: %Schema{ + type: :string, + nullable: true, + description: "User's preferred language for emails" } }, example: %{ diff --git a/lib/pleroma/web/gettext.ex b/lib/pleroma/web/gettext.ex index e85290496e..828b98b153 100644 --- a/lib/pleroma/web/gettext.ex +++ b/lib/pleroma/web/gettext.ex @@ -35,6 +35,14 @@ def language_tag do |> String.replace("_", "-", global: true) end + def normalize_locale(locale) do + if is_binary(locale) do + String.replace(locale, "-", "_") + else + nil + end + end + def supports_locale?(locale) do Pleroma.Web.Gettext |> Gettext.known_locales() diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 2c97cadc29..e1c68a98ed 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -221,7 +221,7 @@ def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _p # Note: param name is indeed :discoverable (not an error) |> Maps.put_if_present(:is_discoverable, params[:discoverable]) |> Maps.put_if_present(:birthday, params[:birthday]) - |> Maps.put_if_present(:language, params[:language]) + |> Maps.put_if_present(:language, Pleroma.Web.Gettext.normalize_locale(params[:language])) # What happens here: # diff --git a/lib/pleroma/web/plugs/set_locale_plug.ex b/lib/pleroma/web/plugs/set_locale_plug.ex index 3c3fffa81a..4c6e44fb59 100644 --- a/lib/pleroma/web/plugs/set_locale_plug.ex +++ b/lib/pleroma/web/plugs/set_locale_plug.ex @@ -25,7 +25,7 @@ defp get_locale_from_header(conn) do defp normalize_language_codes(codes) do codes - |> Enum.map(fn code -> String.replace(code, "-", "_") end) + |> Enum.map(fn code -> Pleroma.Web.Gettext.normalize_locale(code) end) end defp extract_preferred_language(conn) do diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index aa4dfb1457..a8a931f2ad 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -12,6 +12,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do alias Pleroma.UserInviteToken def register_user(params, opts \\ []) do + fallback_language = Gettext.get_locale() + params = params |> Map.take([:email, :token, :password]) @@ -21,6 +23,10 @@ def register_user(params, opts \\ []) do |> Map.put(:password_confirmation, params[:password]) |> Map.put(:registration_reason, params[:reason]) |> Map.put(:birthday, params[:birthday]) + |> Map.put( + :language, + Pleroma.Web.Gettext.normalize_locale(params[:language]) || fallback_language + ) if Pleroma.Config.get([:instance, :registrations_open]) do create_user(params, opts) diff --git a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs index f272ed1ae1..d5978372b7 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -13,6 +13,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do alias Pleroma.Web.ActivityPub.InternalFetchActor alias Pleroma.Web.CommonAPI alias Pleroma.Web.OAuth.Token + alias Pleroma.Web.Plugs.SetLocalePlug import Pleroma.Factory @@ -1662,6 +1663,75 @@ test "returns an error if missing birth date", %{conn: conn} do end end + describe "create account with language" do + setup %{conn: conn} do + app_token = insert(:oauth_token, user: nil) + + conn = + conn + |> put_req_header("authorization", "Bearer " <> app_token.token) + |> put_req_header("content-type", "multipart/form-data") + |> put_req_cookie(SetLocalePlug.frontend_language_cookie_name(), "zh-Hans") + |> SetLocalePlug.call([]) + + [conn: conn] + end + + test "creates an account with language parameter", %{conn: conn} do + params = %{ + username: "foo", + email: "foo@example.org", + password: "dupa.8", + agreement: true, + language: "ru" + } + + res = + conn + |> post("/api/v1/accounts", params) + + assert json_response_and_validate_schema(res, 200) + + assert %{language: "ru"} = Pleroma.User.get_by_nickname("foo") + end + + test "language parameter should be normalized", %{conn: conn} do + params = %{ + username: "foo", + email: "foo@example.org", + password: "dupa.8", + agreement: true, + language: "ru-RU" + } + + res = + conn + |> post("/api/v1/accounts", params) + + assert json_response_and_validate_schema(res, 200) + + assert %{language: "ru_RU"} = Pleroma.User.get_by_nickname("foo") + end + + test "createing an account without language parameter should fallback to cookie/header language", + %{conn: conn} do + params = %{ + username: "foo2", + email: "foo2@example.org", + password: "dupa.8", + agreement: true + } + + res = + conn + |> post("/api/v1/accounts", params) + + assert json_response_and_validate_schema(res, 200) + + assert %{language: "zh_Hans"} = Pleroma.User.get_by_nickname("foo2") + end + end + describe "GET /api/v1/accounts/:id/lists - account_lists" do test "returns lists to which the account belongs" do %{user: user, conn: conn} = oauth_access(["read:lists"]) From 5e388870808428c7c57c48c4dd081dd70d19dd2e Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 2 Mar 2022 09:47:51 -0500 Subject: [PATCH 19/37] Document API addition --- docs/development/API/differences_in_mastoapi_responses.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/development/API/differences_in_mastoapi_responses.md b/docs/development/API/differences_in_mastoapi_responses.md index 0e6bcb79b7..73c46fff81 100644 --- a/docs/development/API/differences_in_mastoapi_responses.md +++ b/docs/development/API/differences_in_mastoapi_responses.md @@ -241,6 +241,7 @@ Additional parameters can be added to the JSON body/Form data: - `discoverable` - if true, external services (search bots) etc. are allowed to index / list the account (regardless of this setting, user will still appear in regular search results). - `actor_type` - the type of this account. - `accepts_chat_messages` - if false, this account will reject all chat messages. +- `language` - user's preferred language for receiving emails (digest, confirmation, etc.) All images (avatar, banner and background) can be reset to the default by sending an empty string ("") instead of a file. @@ -292,6 +293,7 @@ Has these additional parameters (which are the same as in Pleroma-API): - `captcha_token`: optional, contains provider-specific captcha token - `captcha_answer_data`: optional, contains provider-specific captcha data - `token`: invite token required when the registrations aren't public. +- `language`: optional, user's preferred language for receiving emails (digest, confirmation, etc.), default to the language set in the `userLanguage` cookies or `Accept-Language` header. ## Instance From 1a917cfeec3057fca9c5d1467fd9bf1401d27d42 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 2 Mar 2022 09:51:46 -0500 Subject: [PATCH 20/37] Add changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e6e0fdf24..6a299804c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - MastoAPI: Support for `birthday` and `show_birthday` field in `/api/v1/accounts/update_credentials`. - Configuration: Add `birthday_required` and `birthday_min_age` settings to provide a way to require users to enter their birth date. - PleromaAPI: Add `GET /api/v1/pleroma/birthdays` API endpoint +- Make backend-rendered pages translatable. This includes emails. Pages returned as a HTTP response are translated using the language specified in the `userLanguage` cookie, or the `Accept-Language` header. Emails are translated using the `language` field when registering. This language can be changed by `PATCH /api/v1/accounts/update_credentials` with the `language` field. ### Fixed - Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies From 8de573b04783ef50b74bd629843a58b37c0ce31d Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 2 Mar 2022 19:59:11 -0500 Subject: [PATCH 21/37] Fallback to a variant if the language in general is not supported For an example, here, zh is not supported, but zh_Hans and zh_Hant are. If the user asks for zh, we should choose a variant for them instead of fallbacking to default. Some browsers (e.g. Firefox) does not allow users to customize their language codes. For example, there is no zh-Hans, but only zh, zh-CN, zh-TW, zh-HK, etc. This provides a workaround for those users suffering from bad design decisions. --- lib/pleroma/web/gettext.ex | 14 ++++++++++++++ lib/pleroma/web/plugs/set_locale_plug.ex | 6 ++++++ test/pleroma/web/plugs/set_locale_plug_test.exs | 14 ++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/lib/pleroma/web/gettext.ex b/lib/pleroma/web/gettext.ex index 828b98b153..cfd92f991e 100644 --- a/lib/pleroma/web/gettext.ex +++ b/lib/pleroma/web/gettext.ex @@ -49,6 +49,20 @@ def supports_locale?(locale) do |> Enum.member?(locale) end + def variant?(locale), do: String.contains?(locale, "_") + + def supported_variants_of_locale(locale) do + cond do + variant?(locale) -> + [locale] + supports_locale?(locale) -> + [locale] + true -> + Gettext.known_locales(Pleroma.Web.Gettext) + |> Enum.filter(fn l -> String.starts_with?(l, locale <> "_") end) + end + end + def locale_or_default(locale) do if supports_locale?(locale) do locale diff --git a/lib/pleroma/web/plugs/set_locale_plug.ex b/lib/pleroma/web/plugs/set_locale_plug.ex index 4c6e44fb59..78ae566c77 100644 --- a/lib/pleroma/web/plugs/set_locale_plug.ex +++ b/lib/pleroma/web/plugs/set_locale_plug.ex @@ -20,6 +20,12 @@ defp get_locale_from_header(conn) do conn |> extract_preferred_language() |> normalize_language_codes() + |> first_supported() + end + + defp first_supported(locales) do + locales + |> Enum.flat_map(&Pleroma.Web.Gettext.supported_variants_of_locale/1) |> Enum.find(&supported_locale?/1) end diff --git a/test/pleroma/web/plugs/set_locale_plug_test.exs b/test/pleroma/web/plugs/set_locale_plug_test.exs index 043d7eb182..349326c245 100644 --- a/test/pleroma/web/plugs/set_locale_plug_test.exs +++ b/test/pleroma/web/plugs/set_locale_plug_test.exs @@ -47,6 +47,20 @@ test "use supported locale with specifiers from `accept-language`" do assert %{locale: "zh_Hans"} == conn.assigns end + test "fallback to some variant of the language if the unqualified language is not supported" do + conn = + :get + |> conn("/cofe") + |> Conn.put_req_header( + "accept-language", + "zh;q=0.9, en;q=0.8, *;q=0.5" + ) + |> SetLocalePlug.call([]) + + assert "zh_" <> _ = Gettext.get_locale() + assert %{locale: "zh_" <> _} = conn.assigns + end + test "use supported locale from cookie" do conn = :get From bc59da96c52a0fe751154dd98405e11817029b23 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 2 Mar 2022 20:04:30 -0500 Subject: [PATCH 22/37] Add test for fallbacking to a general language --- test/pleroma/web/plugs/set_locale_plug_test.exs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/pleroma/web/plugs/set_locale_plug_test.exs b/test/pleroma/web/plugs/set_locale_plug_test.exs index 349326c245..f5d3ab995d 100644 --- a/test/pleroma/web/plugs/set_locale_plug_test.exs +++ b/test/pleroma/web/plugs/set_locale_plug_test.exs @@ -33,6 +33,20 @@ test "use supported locale from `accept-language`" do assert %{locale: "ru"} == conn.assigns end + test "fallback to the general language if a variant is not supported" do + conn = + :get + |> conn("/cofe") + |> Conn.put_req_header( + "accept-language", + "ru-CA;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 supported locale with specifiers from `accept-language`" do conn = :get From d3f3f30c6a7fae04af27d256d0c2fd90ce03adf0 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 2 Mar 2022 22:56:19 -0500 Subject: [PATCH 23/37] Make lint happy --- lib/pleroma/web/gettext.ex | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/pleroma/web/gettext.ex b/lib/pleroma/web/gettext.ex index cfd92f991e..694ad8ad67 100644 --- a/lib/pleroma/web/gettext.ex +++ b/lib/pleroma/web/gettext.ex @@ -55,8 +55,10 @@ def supported_variants_of_locale(locale) do cond do variant?(locale) -> [locale] + supports_locale?(locale) -> [locale] + true -> Gettext.known_locales(Pleroma.Web.Gettext) |> Enum.filter(fn l -> String.starts_with?(l, locale <> "_") end) From 7ea330b4fe1c93eb7caba2631e1adf133708fa20 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Thu, 3 Mar 2022 02:03:44 -0500 Subject: [PATCH 24/37] 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 --- lib/pleroma/web/gettext.ex | 57 ++++++++++++++++++- lib/pleroma/web/plugs/set_locale_plug.ex | 19 ++++--- .../web/plugs/set_locale_plug_test.exs | 30 +++++++--- 3 files changed, 89 insertions(+), 17 deletions(-) diff --git a/lib/pleroma/web/gettext.ex b/lib/pleroma/web/gettext.ex index 694ad8ad67..e17451c09b 100644 --- a/lib/pleroma/web/gettext.ex +++ b/lib/pleroma/web/gettext.ex @@ -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 diff --git a/lib/pleroma/web/plugs/set_locale_plug.ex b/lib/pleroma/web/plugs/set_locale_plug.ex index 78ae566c77..936f65f5da 100644 --- a/lib/pleroma/web/plugs/set_locale_plug.ex +++ b/lib/pleroma/web/plugs/set_locale_plug.ex @@ -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 diff --git a/test/pleroma/web/plugs/set_locale_plug_test.exs b/test/pleroma/web/plugs/set_locale_plug_test.exs index f5d3ab995d..b0e7afffdc 100644 --- a/test/pleroma/web/plugs/set_locale_plug_test.exs +++ b/test/pleroma/web/plugs/set_locale_plug_test.exs @@ -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 From aca11fb70ef7d9f4004d6efd10fb39261f476852 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Thu, 3 Mar 2022 02:31:36 -0500 Subject: [PATCH 25/37] Support multiple locales from userLanguage cookie --- lib/pleroma/web/gettext.ex | 27 ++++++++++++++----- lib/pleroma/web/plugs/set_locale_plug.ex | 14 +++------- .../web/plugs/set_locale_plug_test.exs | 15 +++++++++++ 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/lib/pleroma/web/gettext.ex b/lib/pleroma/web/gettext.ex index e17451c09b..cd795008d0 100644 --- a/lib/pleroma/web/gettext.ex +++ b/lib/pleroma/web/gettext.ex @@ -37,7 +37,7 @@ def language_tag do def normalize_locale(locale) do if is_binary(locale) do - String.replace(locale, "-", "_") + String.replace(locale, "-", "_", global: true) else nil end @@ -51,13 +51,28 @@ def supports_locale?(locale) do def variant?(locale), do: String.contains?(locale, "_") - def supported_variants_of_locale(locale) do - cond do - variant?(locale) -> - [locale] + def language_for_variant(locale) do + Enum.at(String.split(locale, "_"), 0) + end + def ensure_fallbacks(locales) do + locales + |> Enum.flat_map(fn locale -> + others = other_supported_variants_of_locale(locale) + |> Enum.filter(fn l -> not Enum.member?(locales, l) end) + + [locale] ++ others + end) + end + + def other_supported_variants_of_locale(locale) do + cond do supports_locale?(locale) -> - [locale] + [] + + variant?(locale) -> + lang = language_for_variant(locale) + if supports_locale?(lang), do: [lang], else: [] true -> Gettext.known_locales(Pleroma.Web.Gettext) diff --git a/lib/pleroma/web/plugs/set_locale_plug.ex b/lib/pleroma/web/plugs/set_locale_plug.ex index 936f65f5da..e78917199b 100644 --- a/lib/pleroma/web/plugs/set_locale_plug.ex +++ b/lib/pleroma/web/plugs/set_locale_plug.ex @@ -26,11 +26,12 @@ defp get_locales_from_header(conn) do |> extract_preferred_language() |> normalize_language_codes() |> all_supported() + |> Enum.uniq() end defp all_supported(locales) do locales - |> Enum.flat_map(&Pleroma.Web.Gettext.supported_variants_of_locale/1) + |> Pleroma.Web.Gettext.ensure_fallbacks() |> Enum.filter(&supported_locale?/1) end @@ -53,8 +54,7 @@ defp extract_frontend_language(conn) do [] fe_lang -> - [fe_lang] - |> ensure_language_fallbacks() + String.split(fe_lang, ",") end end @@ -67,7 +67,6 @@ defp extract_accept_language(conn) do |> Enum.sort(&(&1.quality > &2.quality)) |> Enum.map(& &1.tag) |> Enum.reject(&is_nil/1) - |> ensure_language_fallbacks() _ -> [] @@ -89,11 +88,4 @@ defp parse_language_option(string) do %{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 diff --git a/test/pleroma/web/plugs/set_locale_plug_test.exs b/test/pleroma/web/plugs/set_locale_plug_test.exs index b0e7afffdc..ff04a859e9 100644 --- a/test/pleroma/web/plugs/set_locale_plug_test.exs +++ b/test/pleroma/web/plugs/set_locale_plug_test.exs @@ -75,6 +75,21 @@ test "it assigns all supported locales" do assert %{locale: "ru", locales: ["ru", "fr", "en"]} = conn.assigns end + test "it assigns all supported locales in cookie" do + conn = + :get + |> conn("/cofe") + |> put_req_cookie(SetLocalePlug.frontend_language_cookie_name(), "zh-Hans,uk,zh-Hant") + |> 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 "zh_Hans" == Gettext.get_locale() + assert %{locale: "zh_Hans", locales: ["zh_Hans", "uk", "zh_Hant", "ru", "fr", "en"]} = conn.assigns + end + test "fallback to some variant of the language if the unqualified language is not supported" do conn = :get From cd42e2bed0039ce4939e4c55fb7fcd7cf2568b44 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Thu, 3 Mar 2022 09:40:18 -0500 Subject: [PATCH 26/37] Lint --- lib/pleroma/web/gettext.ex | 5 +++-- test/pleroma/web/plugs/set_locale_plug_test.exs | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/web/gettext.ex b/lib/pleroma/web/gettext.ex index cd795008d0..89feb0bb3f 100644 --- a/lib/pleroma/web/gettext.ex +++ b/lib/pleroma/web/gettext.ex @@ -58,7 +58,8 @@ def language_for_variant(locale) do def ensure_fallbacks(locales) do locales |> Enum.flat_map(fn locale -> - others = other_supported_variants_of_locale(locale) + others = + other_supported_variants_of_locale(locale) |> Enum.filter(fn l -> not Enum.member?(locales, l) end) [locale] ++ others @@ -80,7 +81,7 @@ def other_supported_variants_of_locale(locale) do end end - def get_locales() do + def get_locales do Process.get({Pleroma.Web.Gettext, :locales}, []) end diff --git a/test/pleroma/web/plugs/set_locale_plug_test.exs b/test/pleroma/web/plugs/set_locale_plug_test.exs index ff04a859e9..f9d34bbe48 100644 --- a/test/pleroma/web/plugs/set_locale_plug_test.exs +++ b/test/pleroma/web/plugs/set_locale_plug_test.exs @@ -87,7 +87,9 @@ test "it assigns all supported locales in cookie" do |> SetLocalePlug.call([]) assert "zh_Hans" == Gettext.get_locale() - assert %{locale: "zh_Hans", locales: ["zh_Hans", "uk", "zh_Hant", "ru", "fr", "en"]} = conn.assigns + + assert %{locale: "zh_Hans", locales: ["zh_Hans", "uk", "zh_Hant", "ru", "fr", "en"]} = + conn.assigns end test "fallback to some variant of the language if the unqualified language is not supported" do From 9b69ccb35b8521efa58d40931b9a83266ad2f686 Mon Sep 17 00:00:00 2001 From: sleepycrow Date: Sun, 6 Mar 2022 14:24:32 +0100 Subject: [PATCH 27/37] Update Caddyfile to Caddy v2 --- installation/caddyfile-pleroma.example | 31 +++++--------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/installation/caddyfile-pleroma.example b/installation/caddyfile-pleroma.example index 7985d9c674..cc7dda0116 100644 --- a/installation/caddyfile-pleroma.example +++ b/installation/caddyfile-pleroma.example @@ -5,34 +5,13 @@ # 2. Copy this section into your Caddyfile and restart Caddy. example.tld { - log /var/log/caddy/pleroma_access.log - errors /var/log/caddy/pleroma_error.log + log { + output file /var/log/caddy/pleroma.log + } - gzip + encode gzip # this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only # and `localhost.` resolves to [::0] on some systems: see issue #930 - proxy / 127.0.0.1:4000 { - websocket - transparent - } - - tls { - # Remove the rest of the lines in here, if you want to support older devices - key_type p256 - ciphers ECDHE-ECDSA-WITH-CHACHA20-POLY1305 ECDHE-RSA-WITH-CHACHA20-POLY1305 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-GCM-SHA256 - } - - # If you do not want to use the mediaproxy function, remove these lines. - # To use this directive, you need the http.cache plugin for Caddy. - cache { - match_path /media - default_max_age 720m - } - - cache { - match_path /proxy - default_max_age 720m - } - # Stop removing lines here. + reverse_proxy 127.0.0.1:4000 } From 89667189b840fc79d85336739e6b2512684d7be0 Mon Sep 17 00:00:00 2001 From: Ilja Date: Sun, 6 Mar 2022 17:36:30 +0100 Subject: [PATCH 28/37] Delete report notifs when demoting from superuser When someone isn't a superuser any more, they shouldn't see the reporsts any more either. Here we delete the report notifications from a user when that user gets updated from being a superuser to a non-superuser. --- lib/pleroma/notification.ex | 8 ++++++++ lib/pleroma/user.ex | 19 ++++++++++++++++++- test/pleroma/notification_test.exs | 19 +++++++++++++++++++ test/pleroma/user_test.exs | 21 +++++++++++++++++++++ 4 files changed, 66 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 9e0ce0329e..2ab09495d0 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -341,6 +341,14 @@ def destroy_multiple(%{id: user_id} = _user, ids) do |> Repo.delete_all() end + def destroy_multiple_from_types(%{id: user_id}, types) do + from(n in Notification, + where: n.user_id == ^user_id, + where: n.type in ^types + ) + |> Repo.delete_all() + end + def dismiss(%Pleroma.Activity{} = activity) do Notification |> where([n], n.activity_id == ^activity.id) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 36177bda3d..7ecd37337d 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1127,10 +1127,27 @@ def update_and_set_cache(struct, params) do |> update_and_set_cache() end - def update_and_set_cache(changeset) do + def update_and_set_cache(%{data: %Pleroma.User{} = user} = changeset) do + was_superuser_before_update = User.superuser?(user) + with {:ok, user} <- Repo.update(changeset, stale_error_field: :id) do set_cache(user) end + |> maybe_remove_report_notifications(was_superuser_before_update) + end + + defp maybe_remove_report_notifications( + {:ok, %Pleroma.User{} = user} = result, + was_superuser_before_update + ) do + if was_superuser_before_update and not User.superuser?(user), + do: user |> Notification.destroy_multiple_from_types(["pleroma:report"]) + + result + end + + defp maybe_remove_report_notifications(result, _) do + result end def get_user_friends_ap_ids(user) do diff --git a/test/pleroma/notification_test.exs b/test/pleroma/notification_test.exs index 716af496d8..b47edd0a36 100644 --- a/test/pleroma/notification_test.exs +++ b/test/pleroma/notification_test.exs @@ -520,6 +520,25 @@ test "it clears all notifications belonging to the user" do end end + describe "destroy_multiple_from_types/2" do + test "clears all notifications of a certain type for a given user" do + report_activity = insert(:report_activity) + user1 = insert(:user, is_moderator: true, is_admin: true) + user2 = insert(:user, is_moderator: true, is_admin: true) + {:ok, _} = Notification.create_notifications(report_activity) + + {:ok, _} = + CommonAPI.post(user2, %{ + status: "hey @#{user1.nickname} !" + }) + + Notification.destroy_multiple_from_types(user1, ["pleroma:report"]) + + assert [%Pleroma.Notification{type: "mention"}] = Notification.for_user(user1) + assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user2) + end + end + describe "set_read_up_to()" do test "it sets all notifications as read up to a specified notification ID" do user = insert(:user) diff --git a/test/pleroma/user_test.exs b/test/pleroma/user_test.exs index a9a3004a84..d44e1bc36c 100644 --- a/test/pleroma/user_test.exs +++ b/test/pleroma/user_test.exs @@ -5,6 +5,7 @@ defmodule Pleroma.UserTest do alias Pleroma.Activity alias Pleroma.Builders.UserBuilder + alias Pleroma.Notification alias Pleroma.Object alias Pleroma.Repo alias Pleroma.Tests.ObanHelpers @@ -2210,6 +2211,26 @@ test "performs update cache if user updated" do assert {:ok, user} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}") assert %User{bio: "test-bio"} = User.get_cached_by_ap_id(user.ap_id) end + + test "removes report notifs when user isn't superuser any more" do + report_activity = insert(:report_activity) + user = insert(:user, is_moderator: true, is_admin: true) + {:ok, _} = Notification.create_notifications(report_activity) + + assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user) + + {:ok, user} = user |> User.admin_api_update(%{is_moderator: false}) + # is still superuser because still admin + assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user) + + {:ok, user} = user |> User.admin_api_update(%{is_moderator: true, is_admin: false}) + # is still superuser because still moderator + assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user) + + {:ok, user} = user |> User.admin_api_update(%{is_moderator: false}) + # is not a superuser any more + assert [] = Notification.for_user(user) + end end describe "following/followers synchronization" do From 79ccb6b9998ffffa32ba059c8e97f0f604db81f6 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 6 Mar 2022 11:43:31 -0500 Subject: [PATCH 29/37] Support fallbacking to other languages --- lib/pleroma/web/gettext.ex | 53 ++++++ mix.exs | 5 +- mix.lock | 2 +- .../en_test/LC_MESSAGES/static_pages.po | 8 +- test/pleroma/web/gettext_test.exs | 162 ++++++++++++++++++ 5 files changed, 224 insertions(+), 6 deletions(-) create mode 100644 test/pleroma/web/gettext_test.exs diff --git a/lib/pleroma/web/gettext.ex b/lib/pleroma/web/gettext.ex index 89feb0bb3f..51e56939e4 100644 --- a/lib/pleroma/web/gettext.ex +++ b/lib/pleroma/web/gettext.ex @@ -118,6 +118,7 @@ def with_locales_func(locales, fun) do put_locales(prev_locales) else Process.delete({Pleroma.Web.Gettext, :locales}) + Process.delete(Gettext) end end end @@ -149,4 +150,56 @@ defmacro with_locale_or_default(locale, do: fun) do ) end end + + defp next_locale(locale, list) do + index = Enum.find_index(list, fn item -> item == locale end) + + if not is_nil(index) do + Enum.at(list, index + 1) + else + nil + end + end + + def handle_missing_translation(locale, domain, msgctxt, msgid, bindings) do + next = next_locale(locale, get_locales()) + + if is_nil(next) do + super(locale, domain, msgctxt, msgid, bindings) + else + {:ok, + Gettext.with_locale(next, fn -> + Gettext.dpgettext(Pleroma.Web.Gettext, domain, msgctxt, msgid, bindings) + end)} + end + end + + def handle_missing_plural_translation( + locale, + domain, + msgctxt, + msgid, + msgid_plural, + n, + bindings + ) do + next = next_locale(locale, get_locales()) + + if is_nil(next) do + super(locale, domain, msgctxt, msgid, msgid_plural, n, bindings) + else + {:ok, + Gettext.with_locale(next, fn -> + Gettext.dpngettext( + Pleroma.Web.Gettext, + domain, + msgctxt, + msgid, + msgid_plural, + n, + bindings + ) + end)} + end + end end diff --git a/mix.exs b/mix.exs index 4387cb0aaa..d732a28868 100644 --- a/mix.exs +++ b/mix.exs @@ -124,7 +124,10 @@ defp deps do {:ecto_sql, "~> 3.6.2"}, {:postgrex, ">= 0.15.5"}, {:oban, "~> 2.3.4"}, - {:gettext, "~> 0.18"}, + {:gettext, + git: "https://github.com/tusooa/gettext.git", + ref: "72fb2496b6c5280ed911bdc3756890e7f38a4808", + override: true}, {:bcrypt_elixir, "~> 2.2"}, {:trailing_format_plug, "~> 0.0.7"}, {:fast_sanitize, "~> 0.2.0"}, diff --git a/mix.lock b/mix.lock index 817240538a..25d52d41c1 100644 --- a/mix.lock +++ b/mix.lock @@ -55,7 +55,7 @@ "gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm", "29bd14a88030980849c7ed2447b8db6d6c9278a28b11a44cafe41b791205440f"}, "gen_stage": {:hex, :gen_stage, "0.14.3", "d0c66f1c87faa301c1a85a809a3ee9097a4264b2edf7644bf5c123237ef732bf", [:mix], [], "hexpm"}, "gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"}, - "gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"}, + "gettext": {:git, "https://github.com/tusooa/gettext.git", "72fb2496b6c5280ed911bdc3756890e7f38a4808", [ref: "72fb2496b6c5280ed911bdc3756890e7f38a4808"]}, "gun": {:hex, :gun, "2.0.0-rc.2", "7c489a32dedccb77b6e82d1f3c5a7dadfbfa004ec14e322cdb5e579c438632d2", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "6b9d1eae146410d727140dbf8b404b9631302ecc2066d1d12f22097ad7d254fc"}, "hackney": {:hex, :hackney, "1.18.0", "c4443d960bb9fba6d01161d01cd81173089686717d9490e5d3606644c48d121f", [:rebar3], [{:certifi, "~>2.8.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "9afcda620704d720db8c6a3123e9848d09c87586dc1c10479c42627b905b5c5e"}, "html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"}, diff --git a/priv/gettext/en_test/LC_MESSAGES/static_pages.po b/priv/gettext/en_test/LC_MESSAGES/static_pages.po index a3378089cd..1a3b7b355b 100644 --- a/priv/gettext/en_test/LC_MESSAGES/static_pages.po +++ b/priv/gettext/en_test/LC_MESSAGES/static_pages.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"PO-Revision-Date: 2022-03-01 21:15-0500\n" +"PO-Revision-Date: 2022-03-06 11:27-0500\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" @@ -423,8 +423,8 @@ msgstr "" msgctxt "new followers count header" msgid "%{count} New Follower" msgid_plural "%{count} New Followers" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "xx%{count} New Followerxx" +msgstr[1] "xx%{count} New Followersxx" #, elixir-format #: lib/pleroma/emails/user_email.ex:356 @@ -466,7 +466,7 @@ msgstr "" #: lib/pleroma/emails/user_email.ex:310 msgctxt "digest email subject" msgid "Your digest from %{instance_name}" -msgstr "" +msgstr "xxYour digest from %{instance_name}xx" #, elixir-format #: lib/pleroma/emails/user_email.ex:81 diff --git a/test/pleroma/web/gettext_test.exs b/test/pleroma/web/gettext_test.exs new file mode 100644 index 0000000000..9ede4827e8 --- /dev/null +++ b/test/pleroma/web/gettext_test.exs @@ -0,0 +1,162 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.GettextTest do + use ExUnit.Case + + require Pleroma.Web.Gettext + + test "put_locales/1: set the first in the list to Gettext's locale" do + Pleroma.Web.Gettext.put_locales(["zh_Hans", "en_test"]) + + assert "zh_Hans" == Gettext.get_locale(Pleroma.Web.Gettext) + end + + test "with_locales/2: reset locale on exit" do + old_first_locale = Gettext.get_locale(Pleroma.Web.Gettext) + old_locales = Pleroma.Web.Gettext.get_locales() + + Pleroma.Web.Gettext.with_locales ["zh_Hans", "en_test"] do + assert "zh_Hans" == Gettext.get_locale(Pleroma.Web.Gettext) + assert ["zh_Hans", "en_test"] == Pleroma.Web.Gettext.get_locales() + end + + assert old_first_locale == Gettext.get_locale(Pleroma.Web.Gettext) + assert old_locales == Pleroma.Web.Gettext.get_locales() + end + + describe "handle_missing_translation/5" do + test "fallback to next locale if some translation is not available" do + Pleroma.Web.Gettext.with_locales ["x_unsupported", "en_test"] do + assert "xxYour account is awaiting approvalxx" == + Pleroma.Web.Gettext.dpgettext( + "static_pages", + "approval pending email subject", + "Your account is awaiting approval" + ) + end + end + + test "duplicated locale in list should not result in infinite loops" do + Pleroma.Web.Gettext.with_locales ["x_unsupported", "x_unsupported", "en_test"] do + assert "xxYour account is awaiting approvalxx" == + Pleroma.Web.Gettext.dpgettext( + "static_pages", + "approval pending email subject", + "Your account is awaiting approval" + ) + end + end + + test "direct interpolation" do + Pleroma.Web.Gettext.with_locales ["en_test"] do + assert "xxYour digest from some instancexx" == + Pleroma.Web.Gettext.dpgettext( + "static_pages", + "digest email subject", + "Your digest from %{instance_name}", + instance_name: "some instance" + ) + end + end + + test "fallback with interpolation" do + Pleroma.Web.Gettext.with_locales ["x_unsupported", "en_test"] do + assert "xxYour digest from some instancexx" == + Pleroma.Web.Gettext.dpgettext( + "static_pages", + "digest email subject", + "Your digest from %{instance_name}", + instance_name: "some instance" + ) + end + end + + test "fallback to msgid" do + Pleroma.Web.Gettext.with_locales ["x_unsupported"] do + assert "Your digest from some instance" == + Pleroma.Web.Gettext.dpgettext( + "static_pages", + "digest email subject", + "Your digest from %{instance_name}", + instance_name: "some instance" + ) + end + end + end + + describe "handle_missing_plural_translation/7" do + test "direct interpolation" do + Pleroma.Web.Gettext.with_locales ["en_test"] do + assert "xx1 New Followerxx" == + Pleroma.Web.Gettext.dpngettext( + "static_pages", + "new followers count header", + "%{count} New Follower", + "%{count} New Followers", + 1, + count: 1 + ) + + assert "xx5 New Followersxx" == + Pleroma.Web.Gettext.dpngettext( + "static_pages", + "new followers count header", + "%{count} New Follower", + "%{count} New Followers", + 5, + count: 5 + ) + end + end + + test "fallback with interpolation" do + Pleroma.Web.Gettext.with_locales ["x_unsupported", "en_test"] do + assert "xx1 New Followerxx" == + Pleroma.Web.Gettext.dpngettext( + "static_pages", + "new followers count header", + "%{count} New Follower", + "%{count} New Followers", + 1, + count: 1 + ) + + assert "xx5 New Followersxx" == + Pleroma.Web.Gettext.dpngettext( + "static_pages", + "new followers count header", + "%{count} New Follower", + "%{count} New Followers", + 5, + count: 5 + ) + end + end + + test "fallback to msgid" do + Pleroma.Web.Gettext.with_locales ["x_unsupported"] do + assert "1 New Follower" == + Pleroma.Web.Gettext.dpngettext( + "static_pages", + "new followers count header", + "%{count} New Follower", + "%{count} New Followers", + 1, + count: 1 + ) + + assert "5 New Followers" == + Pleroma.Web.Gettext.dpngettext( + "static_pages", + "new followers count header", + "%{count} New Follower", + "%{count} New Followers", + 5, + count: 5 + ) + end + end + end +end From cdc5bbe8369d4fc66d642bb3e845a237d11e34d7 Mon Sep 17 00:00:00 2001 From: Ilja Date: Mon, 7 Mar 2022 14:00:42 +0100 Subject: [PATCH 30/37] After code review Use patern matching to see if someone was superuser before --- lib/pleroma/user.ex | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 7ecd37337d..2f3c6eb84a 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1136,11 +1136,8 @@ def update_and_set_cache(%{data: %Pleroma.User{} = user} = changeset) do |> maybe_remove_report_notifications(was_superuser_before_update) end - defp maybe_remove_report_notifications( - {:ok, %Pleroma.User{} = user} = result, - was_superuser_before_update - ) do - if was_superuser_before_update and not User.superuser?(user), + defp maybe_remove_report_notifications({:ok, %Pleroma.User{} = user} = result, true) do + if not User.superuser?(user), do: user |> Notification.destroy_multiple_from_types(["pleroma:report"]) result From 5f37db330f28556fa6c4c7b2559e6b4663ab4417 Mon Sep 17 00:00:00 2001 From: Ilja Date: Tue, 5 Apr 2022 13:21:09 +0200 Subject: [PATCH 31/37] Fix eratic test for POST /api/pleroma/admin/reports/:id/notes It retrieved two ReportNotes and then checked one of them. But the order isn't guaranteed, while the test tested on the content of the first ReportNote. I made the test on the content more generic --- .../web/admin_api/controllers/report_controller_test.exs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/pleroma/web/admin_api/controllers/report_controller_test.exs b/test/pleroma/web/admin_api/controllers/report_controller_test.exs index 6211956c62..30dcb87e2c 100644 --- a/test/pleroma/web/admin_api/controllers/report_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/report_controller_test.exs @@ -355,7 +355,6 @@ test "it creates report note", %{admin_id: admin_id, report_id: report_id} do } = note end - @tag :erratic test "it returns reports with notes", %{conn: conn, admin: admin} do conn = get(conn, "/api/pleroma/admin/reports") @@ -364,7 +363,8 @@ test "it returns reports with notes", %{conn: conn, admin: admin} do [note, _] = notes assert note["user"]["nickname"] == admin.nickname - assert note["content"] == "this is disgusting!" + # We use '=~' because the order of the notes isn't guaranteed + assert note["content"] =~ "this is disgusting" assert note["created_at"] assert response["total"] == 1 end From be08d9305b1dba9d146a1a1482e6728efb113285 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 17 Apr 2022 22:39:52 -0400 Subject: [PATCH 32/37] Fix incorrect fallback when English is set to first language --- lib/pleroma/web/gettext.ex | 19 +++++++++++++++++-- test/pleroma/web/gettext_test.exs | 11 +++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/gettext.ex b/lib/pleroma/web/gettext.ex index d5ec13b66c..5ef49d8419 100644 --- a/lib/pleroma/web/gettext.ex +++ b/lib/pleroma/web/gettext.ex @@ -161,10 +161,25 @@ defp next_locale(locale, list) do end end + # We do not yet have a proper English translation. The "English" + # version is currently but the fallback msgid. However, this + # will not work if the user puts English as the first language, + # and at the same time specifies other languages, as gettext will + # think the English translation is missing, and call + # handle_missing_translation functions. This may result in + # text in other languages being shown even if English is preferred + # by the user. + # + # To prevent this, we do not allow fallbacking when the current + # locale missing a translation is English. + defp should_fallback?(locale) do + locale != "en" + end + def handle_missing_translation(locale, domain, msgctxt, msgid, bindings) do next = next_locale(locale, get_locales()) - if is_nil(next) do + if is_nil(next) or not should_fallback?(locale) do super(locale, domain, msgctxt, msgid, bindings) else {:ok, @@ -185,7 +200,7 @@ def handle_missing_plural_translation( ) do next = next_locale(locale, get_locales()) - if is_nil(next) do + if is_nil(next) or not should_fallback?(locale) do super(locale, domain, msgctxt, msgid, msgid_plural, n, bindings) else {:ok, diff --git a/test/pleroma/web/gettext_test.exs b/test/pleroma/web/gettext_test.exs index 9ede4827e8..e186f1ab33 100644 --- a/test/pleroma/web/gettext_test.exs +++ b/test/pleroma/web/gettext_test.exs @@ -38,6 +38,17 @@ test "fallback to next locale if some translation is not available" do end end + test "putting en locale at the front should not make gettext fallback unexpectedly" do + Pleroma.Web.Gettext.with_locales ["en", "en_test"] do + assert "Your account is awaiting approval" == + Pleroma.Web.Gettext.dpgettext( + "static_pages", + "approval pending email subject", + "Your account is awaiting approval" + ) + end + end + test "duplicated locale in list should not result in infinite loops" do Pleroma.Web.Gettext.with_locales ["x_unsupported", "x_unsupported", "en_test"] do assert "xxYour account is awaiting approvalxx" == From c3b2b71ea211d7e377186cace419703f5e3f2d68 Mon Sep 17 00:00:00 2001 From: Ilja Date: Sun, 20 Mar 2022 13:32:12 +0100 Subject: [PATCH 33/37] update sweet_xml [Security] --- mix.exs | 2 +- mix.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index c58a239ceb..39c2b81b6b 100644 --- a/mix.exs +++ b/mix.exs @@ -145,7 +145,7 @@ defp deps do {:mogrify, "~> 0.9.1"}, {:ex_aws, "~> 2.1.6"}, {:ex_aws_s3, "~> 2.0"}, - {:sweet_xml, "~> 0.6.6"}, + {:sweet_xml, "~> 0.7.2"}, {:earmark, "~> 1.4.15"}, {:bbcode_pleroma, "~> 0.2.0"}, {:crypt, diff --git a/mix.lock b/mix.lock index 25d52d41c1..44a8054eaf 100644 --- a/mix.lock +++ b/mix.lock @@ -120,7 +120,7 @@ "remote_ip": {:git, "https://git.pleroma.social/pleroma/remote_ip.git", "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8", [ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"]}, "sleeplocks": {:hex, :sleeplocks, "1.1.1", "3d462a0639a6ef36cc75d6038b7393ae537ab394641beb59830a1b8271faeed3", [:rebar3], [], "hexpm", "84ee37aeff4d0d92b290fff986d6a95ac5eedf9b383fadfd1d88e9b84a1c02e1"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, - "sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm", "2e1ec458f892ffa81f9f8386e3f35a1af6db7a7a37748a64478f13163a1f3573"}, + "sweet_xml": {:hex, :sweet_xml, "0.7.2", "4729f997286811fabdd8288f8474e0840a76573051062f066c4b597e76f14f9f", [:mix], [], "hexpm", "6894e68a120f454534d99045ea3325f7740ea71260bc315f82e29731d570a6e8"}, "swoosh": {:hex, :swoosh, "1.3.11", "34f79c57f19892b43bd2168de9ff5de478a721a26328ef59567aad4243e7a77b", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "f1e2a048db454f9982b9cf840f75e7399dd48be31ecc2a7dc10012a803b913af"}, "syslog": {:hex, :syslog, "1.1.0", "6419a232bea84f07b56dc575225007ffe34d9fdc91abe6f1b2f254fd71d8efc2", [:rebar3], [], "hexpm", "4c6a41373c7e20587be33ef841d3de6f3beba08519809329ecc4d27b15b659e1"}, "table_rex": {:hex, :table_rex, "3.1.1", "0c67164d1714b5e806d5067c1e96ff098ba7ae79413cc075973e17c38a587caa", [:mix], [], "hexpm", "678a23aba4d670419c23c17790f9dcd635a4a89022040df7d5d772cb21012490"}, From e2d24eda5745310346b5e347efddbc68723612f0 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Thu, 5 May 2022 18:39:34 -0400 Subject: [PATCH 34/37] Allow to skip cache in Cache plug Ref: fix-local-public --- lib/pleroma/web/plugs/cache.ex | 19 ++++++++++++------- test/pleroma/web/plugs/cache_test.exs | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/lib/pleroma/web/plugs/cache.ex b/lib/pleroma/web/plugs/cache.ex index e2cf5759d2..aaff36407b 100644 --- a/lib/pleroma/web/plugs/cache.ex +++ b/lib/pleroma/web/plugs/cache.ex @@ -98,14 +98,19 @@ defp cache_resp(conn, opts) do content_type = content_type(conn) conn = - unless opts[:tracking_fun] do - @cachex.put(:web_resp_cache, key, {content_type, body}, ttl: ttl) - conn - else - tracking_fun_data = Map.get(conn.assigns, :tracking_fun_data, nil) - @cachex.put(:web_resp_cache, key, {content_type, body, tracking_fun_data}, ttl: ttl) + cond do + Map.get(conn.assigns, :skip_cache, false) -> + conn - opts.tracking_fun.(conn, tracking_fun_data) + !opts[:tracking_fun] -> + @cachex.put(:web_resp_cache, key, {content_type, body}, ttl: ttl) + conn + + true -> + tracking_fun_data = Map.get(conn.assigns, :tracking_fun_data, nil) + @cachex.put(:web_resp_cache, key, {content_type, body, tracking_fun_data}, ttl: ttl) + + opts.tracking_fun.(conn, tracking_fun_data) end put_resp_header(conn, "x-cache", "MISS from Pleroma") diff --git a/test/pleroma/web/plugs/cache_test.exs b/test/pleroma/web/plugs/cache_test.exs index d84e2d2689..0c119528d5 100644 --- a/test/pleroma/web/plugs/cache_test.exs +++ b/test/pleroma/web/plugs/cache_test.exs @@ -179,4 +179,22 @@ test "ignore non-successful responses" do |> send_resp(:im_a_teapot, "🥤") |> sent_resp() end + + test "ignores if skip_cache is assigned" do + assert @miss_resp == + conn(:get, "/") + |> assign(:skip_cache, true) + |> Cache.call(%{query_params: false, ttl: nil}) + |> put_resp_content_type("cofe/hot") + |> send_resp(:ok, "cofe") + |> sent_resp() + + assert @miss_resp == + conn(:get, "/") + |> assign(:skip_cache, true) + |> Cache.call(%{query_params: false, ttl: nil}) + |> put_resp_content_type("cofe/hot") + |> send_resp(:ok, "cofe") + |> sent_resp() + end end From 57c030a0a729f0ee87330d231ca6fb1151840a43 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Thu, 5 May 2022 19:20:32 -0400 Subject: [PATCH 35/37] Skip cache when /objects or /activities is authenticated Ref: fix-local-public --- .../activity_pub/activity_pub_controller.ex | 11 +++++++++ lib/pleroma/web/plugs/cache.ex | 21 +++++++++------- .../activity_pub_controller_test.exs | 24 +++++++++++++++++++ 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 20f8bbc2d0..b8f63d69dc 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -84,6 +84,7 @@ def object(%{assigns: assigns} = conn, _) do user <- Map.get(assigns, :user, nil), {_, true} <- {:visible?, Visibility.visible_for_user?(object, user)} do conn + |> maybe_skip_cache(user) |> assign(:tracking_fun_data, object.id) |> set_cache_ttl_for(object) |> put_resp_content_type("application/activity+json") @@ -112,6 +113,7 @@ def activity(%{assigns: assigns} = conn, _) do user <- Map.get(assigns, :user, nil), {_, true} <- {:visible?, Visibility.visible_for_user?(activity, user)} do conn + |> maybe_skip_cache(user) |> maybe_set_tracking_data(activity) |> set_cache_ttl_for(activity) |> put_resp_content_type("application/activity+json") @@ -151,6 +153,15 @@ defp set_cache_ttl_for(conn, entity) do assign(conn, :cache_ttl, ttl) end + def maybe_skip_cache(conn, user) do + if user do + conn + |> assign(:skip_cache, true) + else + conn + end + end + # GET /relay/following def relay_following(conn, _params) do with %{halted: false} = conn <- FederatingPlug.call(conn, []) do diff --git a/lib/pleroma/web/plugs/cache.ex b/lib/pleroma/web/plugs/cache.ex index aaff36407b..6674778579 100644 --- a/lib/pleroma/web/plugs/cache.ex +++ b/lib/pleroma/web/plugs/cache.ex @@ -97,20 +97,23 @@ defp cache_resp(conn, opts) do key = cache_key(conn, opts) content_type = content_type(conn) + should_cache = not Map.get(conn.assigns, :skip_cache, false) + conn = - cond do - Map.get(conn.assigns, :skip_cache, false) -> - conn - - !opts[:tracking_fun] -> + unless opts[:tracking_fun] do + if should_cache do @cachex.put(:web_resp_cache, key, {content_type, body}, ttl: ttl) - conn + end - true -> - tracking_fun_data = Map.get(conn.assigns, :tracking_fun_data, nil) + conn + else + tracking_fun_data = Map.get(conn.assigns, :tracking_fun_data, nil) + + if should_cache do @cachex.put(:web_resp_cache, key, {content_type, body, tracking_fun_data}, ttl: ttl) + end - opts.tracking_fun.(conn, tracking_fun_data) + opts.tracking_fun.(conn, tracking_fun_data) end put_resp_header(conn, "x-cache", "MISS from Pleroma") diff --git a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs index 1923ec4794..1c5c40e848 100644 --- a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs @@ -291,6 +291,30 @@ test "it returns a json representation of the object with accept application/ld+ assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note}) end + test "does not cache authenticated response", %{conn: conn} do + user = insert(:user) + reader = insert(:user) + + {:ok, post} = + CommonAPI.post(user, %{status: "test @#{reader.nickname}", visibility: "local"}) + + object = Object.normalize(post, fetch: false) + uuid = String.split(object.data["id"], "/") |> List.last() + + assert response = + conn + |> assign(:user, reader) + |> put_req_header("accept", "application/activity+json") + |> get("/objects/#{uuid}") + + json_response(response, 200) + + conn + |> put_req_header("accept", "application/activity+json") + |> get("/objects/#{uuid}") + |> json_response(404) + end + test "it returns 404 for non-public messages", %{conn: conn} do note = insert(:direct_note) uuid = String.split(note.data["id"], "/") |> List.last() From f9943b2065598d2154b400170d051656a0107518 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 6 May 2022 08:59:36 +0200 Subject: [PATCH 36/37] mix: Bump to 2.4.52 for 2.4.3 mergeback --- CHANGELOG.md | 6 ++++++ mix.exs | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a299804c1..fc5abb9f12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Removed +## 2.4.3 - 2022-05-06 + +### Security +- Private `/objects/` and `/activities/` leaking if cached by authenticated user +- SweetXML library DTD bomb + ## 2.4.2 - 2022-01-10 ### Fixed diff --git a/mix.exs b/mix.exs index 39c2b81b6b..0651781ccf 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do def project do [ app: :pleroma, - version: version("2.4.51"), + version: version("2.4.52"), elixir: "~> 1.9", elixirc_paths: elixirc_paths(Mix.env()), compilers: [:phoenix, :gettext] ++ Mix.compilers(), From a8093732bd1f90a7b3c83b264cbddb96a578bafe Mon Sep 17 00:00:00 2001 From: Ilja Date: Sun, 8 May 2022 18:10:40 +0000 Subject: [PATCH 37/37] Also use actor_type to determine if an account is a bot in antiFollowbotPolicy --- CHANGELOG.md | 1 + docs/configuration/cheatsheet.md | 1 + .../activity_pub/mrf/anti_followbot_policy.ex | 21 +++++-- .../mrf/anti_followbot_policy_test.exs | 57 +++++++++++++++---- 4 files changed, 65 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e6e0fdf24..8a697ce545 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -90,6 +90,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Improved Twittercard and OpenGraph meta tag generation including thumbnails and image dimension metadata when available. - AdminAPI: sort users so the newest are at the top. - ActivityPub Client-to-Server(C2S): Limitation on the type of Activity/Object are lifted as they are now passed through ObjectValidators +- MRF (`AntiFollowbotPolicy`): Bot accounts are now also considered followbots. Users can still allow bots to follow them by first following the bot. ### Added diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index 4dacdc68c8..1e74d40e62 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -125,6 +125,7 @@ To add configuration to your config file, you can copy it from the base config. * `Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy`: Sets a default expiration on all posts made by users of the local instance. Requires `Pleroma.Workers.PurgeExpiredActivity` to be enabled for processing the scheduled delections. * `Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy`: Makes all bot posts to disappear from public timelines. * `Pleroma.Web.ActivityPub.MRF.FollowBotPolicy`: Automatically follows newly discovered users from the specified bot account. Local accounts, locked accounts, and users with "#nobot" in their bio are respected and excluded from being followed. + * `Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy`: Drops follow requests from followbots. Users can still allow bots to follow them by first following the bot. * `Pleroma.Web.ActivityPub.MRF.KeywordPolicy`: Rejects or removes from the federated timeline or replaces keywords. (See [`:mrf_keyword`](#mrf_keyword)). * `Pleroma.Web.ActivityPub.MRF.ForceMentionsInContent`: Forces every mentioned user to be reflected in the post content. * `transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo). diff --git a/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex index 851e95d226..627f52168d 100644 --- a/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex @@ -24,7 +24,7 @@ defp score_displayname("federationbot"), do: 1.0 defp score_displayname("fedibot"), do: 1.0 defp score_displayname(_), do: 0.0 - defp determine_if_followbot(%User{nickname: nickname, name: displayname}) do + defp determine_if_followbot(%User{nickname: nickname, name: displayname, actor_type: actor_type}) do # nickname will be a binary string except when following a relay nick_score = if is_binary(nickname) do @@ -45,19 +45,32 @@ defp determine_if_followbot(%User{nickname: nickname, name: displayname}) do 0.0 end - nick_score + name_score + # actor_type "Service" is a Bot account + actor_type_score = + if actor_type == "Service" do + 1.0 + else + 0.0 + end + + nick_score + name_score + actor_type_score end defp determine_if_followbot(_), do: 0.0 + defp bot_allowed?(%{"object" => target}, bot_actor) do + %User{} = user = normalize_by_ap_id(target) + + User.following?(user, bot_actor) + end + @impl true def filter(%{"type" => "Follow", "actor" => actor_id} = message) do %User{} = actor = normalize_by_ap_id(actor_id) score = determine_if_followbot(actor) - # TODO: scan biography data for keywords and score it somehow. - if score < 0.8 do + if score < 0.8 || bot_allowed?(message, actor) do {:ok, message} else {:reject, "[AntiFollowbotPolicy] Scored #{actor_id} as #{score}"} diff --git a/test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs b/test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs index d5af3a9b65..14a6ae52b1 100644 --- a/test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs @@ -6,6 +6,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicyTest do use Pleroma.DataCase, async: true import Pleroma.Factory + alias Pleroma.User alias Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy describe "blocking based on attributes" do @@ -38,21 +39,55 @@ test "matches followbots by display name" do assert {:reject, "[AntiFollowbotPolicy]" <> _} = AntiFollowbotPolicy.filter(message) end + + test "matches followbots by actor_type" do + actor = insert(:user, %{actor_type: "Service"}) + target = insert(:user) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "type" => "Follow", + "actor" => actor.ap_id, + "object" => target.ap_id, + "id" => "https://example.com/activities/1234" + } + + assert {:reject, "[AntiFollowbotPolicy]" <> _} = AntiFollowbotPolicy.filter(message) + end end - test "it allows non-followbots" do - actor = insert(:user) - target = insert(:user) + describe "it allows" do + test "non-followbots" do + actor = insert(:user) + target = insert(:user) - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Follow", - "actor" => actor.ap_id, - "object" => target.ap_id, - "id" => "https://example.com/activities/1234" - } + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "type" => "Follow", + "actor" => actor.ap_id, + "object" => target.ap_id, + "id" => "https://example.com/activities/1234" + } - {:ok, _} = AntiFollowbotPolicy.filter(message) + {:ok, _} = AntiFollowbotPolicy.filter(message) + end + + test "bots if the target follows the bots" do + actor = insert(:user, %{actor_type: "Service"}) + target = insert(:user) + + User.follow(target, actor) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "type" => "Follow", + "actor" => actor.ap_id, + "object" => target.ap_id, + "id" => "https://example.com/activities/1234" + } + + {:ok, _} = AntiFollowbotPolicy.filter(message) + end end test "it gracefully handles nil display names" do