about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
authorClaire <claire.github-309c@sitedethib.com>2023-03-31 21:30:27 +0200
committerClaire <claire.github-309c@sitedethib.com>2023-03-31 21:30:27 +0200
commit01d6f7529faef97c0209ef11bbca2e856961bbab (patch)
tree513ac21302befa1a08fc4968dcd4dca6b0e06360 /app
parentcbdb25ab0343603165fc89fd28b07c9ca0f2ae6d (diff)
parentc6c03b49b255c4fe2183b94288a712ad7a66e2c2 (diff)
Merge branch 'main' into glitch-soc/merge-upstream
Conflicts:
- `README.md`:
  Upstream added a link to the roadmap, but we have a completely different README.
  Kept ours.
- `app/models/media_attachment.rb`:
  Upstream upped media attachment limits.
  Updated the default according to upstream's.
- `db/migrate/20180831171112_create_bookmarks.rb`:
  Upstream changed the migration compatibility level.
  Did so too.
- `config/initializers/content_security_policy.rb`:
  Upstream refactored this file but we have a different version.
  Kept our version.
- `app/controllers/settings/preferences_controller.rb`:
  Upstream completely refactored user settings storage, and glitch-soc has a
  different set of settings.
  The file does not directly references individual settings anymore.
  Applied upstream changes.
- `app/lib/user_settings_decorator.rb`:
  Upstream completely refactored user settings storage, and glitch-soc has a
  different set of settings.
  The file got removed entirely.
  Removed it as well.
- `app/models/user.rb`:
  Upstream completely refactored user settings storage, and glitch-soc has a
  different set of settings.
  References to individual settings have been removed from the file.
  Removed them as well.
- `app/views/settings/preferences/appearance/show.html.haml`:
  Upstream completely refactored user settings storage, and glitch-soc has a
  different set of settings.
  Applied upstream's changes and ported ours back.
- `app/views/settings/preferences/notifications/show.html.haml`:
  Upstream completely refactored user settings storage, and glitch-soc has a
  different set of settings.
  Applied upstream's changes and ported ours back.
- `app/views/settings/preferences/other/show.html.haml`:
  Upstream completely refactored user settings storage, and glitch-soc has a
  different set of settings.
  Applied upstream's changes and ported ours back.
- `config/settings.yml`:
  Upstream completely refactored user settings storage, and glitch-soc has a
  different set of settings.
  In particular, upstream removed user-specific and unused settings.
  Did the same in glitch-soc.
- `spec/controllers/application_controller_spec.rb`:
  Conflicts due to glitch-soc's theming system.
  Mostly kept our version, as upstream messed up the tests.
Diffstat (limited to 'app')
-rw-r--r--app/controllers/api/v1/accounts/credentials_controller.rb12
-rw-r--r--app/controllers/api/v1/streaming_controller.rb2
-rw-r--r--app/controllers/backups_controller.rb6
-rw-r--r--app/controllers/concerns/account_controller_concern.rb3
-rw-r--r--app/controllers/media_proxy_controller.rb2
-rw-r--r--app/controllers/settings/preferences_controller.rb43
-rw-r--r--app/controllers/statuses_controller.rb5
-rw-r--r--app/javascript/mastodon/actions/compose.js73
-rw-r--r--app/javascript/mastodon/components/account.jsx53
-rw-r--r--app/javascript/mastodon/components/status.jsx7
-rw-r--r--app/javascript/mastodon/components/status_action_bar.jsx2
-rw-r--r--app/javascript/mastodon/features/account/components/header.jsx2
-rw-r--r--app/javascript/mastodon/features/direct_timeline/index.jsx4
-rw-r--r--app/javascript/mastodon/features/explore/index.jsx4
-rw-r--r--app/javascript/mastodon/features/getting_started/index.jsx2
-rw-r--r--app/javascript/mastodon/features/status/components/action_bar.jsx2
-rw-r--r--app/javascript/mastodon/features/status/components/detailed_status.jsx10
-rw-r--r--app/javascript/mastodon/features/status/index.jsx2
-rw-r--r--app/javascript/mastodon/features/ui/components/navigation_panel.jsx2
-rw-r--r--app/javascript/mastodon/locales/af.json1
-rw-r--r--app/javascript/mastodon/locales/an.json1
-rw-r--r--app/javascript/mastodon/locales/ar.json1
-rw-r--r--app/javascript/mastodon/locales/ast.json1
-rw-r--r--app/javascript/mastodon/locales/be.json3
-rw-r--r--app/javascript/mastodon/locales/bg.json1
-rw-r--r--app/javascript/mastodon/locales/bn.json1
-rw-r--r--app/javascript/mastodon/locales/br.json1
-rw-r--r--app/javascript/mastodon/locales/bs.json1
-rw-r--r--app/javascript/mastodon/locales/ca.json1
-rw-r--r--app/javascript/mastodon/locales/ckb.json1
-rw-r--r--app/javascript/mastodon/locales/co.json1
-rw-r--r--app/javascript/mastodon/locales/cs.json1
-rw-r--r--app/javascript/mastodon/locales/csb.json1
-rw-r--r--app/javascript/mastodon/locales/cy.json1
-rw-r--r--app/javascript/mastodon/locales/da.json1
-rw-r--r--app/javascript/mastodon/locales/de.json1
-rw-r--r--app/javascript/mastodon/locales/defaultMessages.json20
-rw-r--r--app/javascript/mastodon/locales/el.json1
-rw-r--r--app/javascript/mastodon/locales/en-GB.json1
-rw-r--r--app/javascript/mastodon/locales/en.json13
-rw-r--r--app/javascript/mastodon/locales/eo.json1
-rw-r--r--app/javascript/mastodon/locales/es-AR.json1
-rw-r--r--app/javascript/mastodon/locales/es-MX.json1
-rw-r--r--app/javascript/mastodon/locales/es.json1
-rw-r--r--app/javascript/mastodon/locales/et.json3
-rw-r--r--app/javascript/mastodon/locales/eu.json1
-rw-r--r--app/javascript/mastodon/locales/fa.json1
-rw-r--r--app/javascript/mastodon/locales/fi.json85
-rw-r--r--app/javascript/mastodon/locales/fo.json1
-rw-r--r--app/javascript/mastodon/locales/fr-QC.json1
-rw-r--r--app/javascript/mastodon/locales/fr.json1
-rw-r--r--app/javascript/mastodon/locales/fy.json1
-rw-r--r--app/javascript/mastodon/locales/ga.json1
-rw-r--r--app/javascript/mastodon/locales/gd.json1
-rw-r--r--app/javascript/mastodon/locales/gl.json1
-rw-r--r--app/javascript/mastodon/locales/he.json7
-rw-r--r--app/javascript/mastodon/locales/hi.json1
-rw-r--r--app/javascript/mastodon/locales/hr.json1
-rw-r--r--app/javascript/mastodon/locales/hu.json279
-rw-r--r--app/javascript/mastodon/locales/hy.json1
-rw-r--r--app/javascript/mastodon/locales/id.json1
-rw-r--r--app/javascript/mastodon/locales/ig.json1
-rw-r--r--app/javascript/mastodon/locales/io.json1
-rw-r--r--app/javascript/mastodon/locales/is.json1
-rw-r--r--app/javascript/mastodon/locales/it.json1
-rw-r--r--app/javascript/mastodon/locales/ja.json1
-rw-r--r--app/javascript/mastodon/locales/ka.json1
-rw-r--r--app/javascript/mastodon/locales/kab.json1
-rw-r--r--app/javascript/mastodon/locales/kk.json1
-rw-r--r--app/javascript/mastodon/locales/kn.json1
-rw-r--r--app/javascript/mastodon/locales/ko.json3
-rw-r--r--app/javascript/mastodon/locales/ku.json1
-rw-r--r--app/javascript/mastodon/locales/kw.json1
-rw-r--r--app/javascript/mastodon/locales/la.json1
-rw-r--r--app/javascript/mastodon/locales/lt.json1
-rw-r--r--app/javascript/mastodon/locales/lv.json1
-rw-r--r--app/javascript/mastodon/locales/mk.json1
-rw-r--r--app/javascript/mastodon/locales/ml.json1
-rw-r--r--app/javascript/mastodon/locales/mr.json3
-rw-r--r--app/javascript/mastodon/locales/ms.json1
-rw-r--r--app/javascript/mastodon/locales/my.json11
-rw-r--r--app/javascript/mastodon/locales/nl.json1
-rw-r--r--app/javascript/mastodon/locales/nn.json1
-rw-r--r--app/javascript/mastodon/locales/no.json1
-rw-r--r--app/javascript/mastodon/locales/oc.json1
-rw-r--r--app/javascript/mastodon/locales/pa.json1
-rw-r--r--app/javascript/mastodon/locales/pl.json1
-rw-r--r--app/javascript/mastodon/locales/pt-BR.json1
-rw-r--r--app/javascript/mastodon/locales/pt-PT.json1
-rw-r--r--app/javascript/mastodon/locales/ro.json1
-rw-r--r--app/javascript/mastodon/locales/ru.json3
-rw-r--r--app/javascript/mastodon/locales/sa.json1
-rw-r--r--app/javascript/mastodon/locales/sc.json1
-rw-r--r--app/javascript/mastodon/locales/sco.json1
-rw-r--r--app/javascript/mastodon/locales/si.json1
-rw-r--r--app/javascript/mastodon/locales/sk.json3
-rw-r--r--app/javascript/mastodon/locales/sl.json1
-rw-r--r--app/javascript/mastodon/locales/sq.json1
-rw-r--r--app/javascript/mastodon/locales/sr-Latn.json3
-rw-r--r--app/javascript/mastodon/locales/sr.json19
-rw-r--r--app/javascript/mastodon/locales/sv.json1
-rw-r--r--app/javascript/mastodon/locales/szl.json1
-rw-r--r--app/javascript/mastodon/locales/ta.json1
-rw-r--r--app/javascript/mastodon/locales/tai.json1
-rw-r--r--app/javascript/mastodon/locales/te.json1
-rw-r--r--app/javascript/mastodon/locales/th.json5
-rw-r--r--app/javascript/mastodon/locales/tr.json1
-rw-r--r--app/javascript/mastodon/locales/tt.json1
-rw-r--r--app/javascript/mastodon/locales/ug.json1
-rw-r--r--app/javascript/mastodon/locales/uk.json1
-rw-r--r--app/javascript/mastodon/locales/ur.json1
-rw-r--r--app/javascript/mastodon/locales/uz.json1
-rw-r--r--app/javascript/mastodon/locales/vi.json3
-rw-r--r--app/javascript/mastodon/locales/zgh.json1
-rw-r--r--app/javascript/mastodon/locales/zh-CN.json1
-rw-r--r--app/javascript/mastodon/locales/zh-HK.json1
-rw-r--r--app/javascript/mastodon/locales/zh-TW.json1
-rw-r--r--app/javascript/mastodon/performance.js1
-rw-r--r--app/javascript/mastodon/utils/resize_image.js189
-rw-r--r--app/javascript/styles/mastodon-light/diff.scss10
-rw-r--r--app/javascript/styles/mastodon/about.scss4
-rw-r--r--app/javascript/styles/mastodon/accounts.scss16
-rw-r--r--app/javascript/styles/mastodon/admin.scss83
-rw-r--r--app/javascript/styles/mastodon/basics.scss2
-rw-r--r--app/javascript/styles/mastodon/components.scss408
-rw-r--r--app/javascript/styles/mastodon/containers.scss6
-rw-r--r--app/javascript/styles/mastodon/emoji_picker.scss10
-rw-r--r--app/javascript/styles/mastodon/forms.scss34
-rw-r--r--app/javascript/styles/mastodon/modal.scss2
-rw-r--r--app/javascript/styles/mastodon/polls.scss2
-rw-r--r--app/javascript/styles/mastodon/rich_text.scss6
-rw-r--r--app/javascript/styles/mastodon/rtl.scss305
-rw-r--r--app/javascript/styles/mastodon/statuses.scss8
-rw-r--r--app/javascript/styles/mastodon/tables.scss22
-rw-r--r--app/javascript/styles/mastodon/widgets.scss16
-rw-r--r--app/lib/admin/metrics/retention.rb56
-rw-r--r--app/lib/user_settings_decorator.rb180
-rw-r--r--app/lib/user_settings_serializer.rb19
-rw-r--r--app/models/account.rb2
-rw-r--r--app/models/account_stat.rb2
-rw-r--r--app/models/admin/action_log.rb2
-rw-r--r--app/models/backup.rb2
-rw-r--r--app/models/concerns/attachmentable.rb2
-rw-r--r--app/models/concerns/has_user_settings.rb141
-rw-r--r--app/models/custom_filter.rb2
-rw-r--r--app/models/email_domain_block.rb2
-rw-r--r--app/models/media_attachment.rb10
-rw-r--r--app/models/preview_card.rb4
-rw-r--r--app/models/report.rb2
-rw-r--r--app/models/status_edit.rb2
-rw-r--r--app/models/user.rb67
-rw-r--r--app/models/user_settings.rb99
-rw-r--r--app/models/user_settings/dsl.rb37
-rw-r--r--app/models/user_settings/glue.rb23
-rw-r--r--app/models/user_settings/namespace.rb21
-rw-r--r--app/models/user_settings/setting.rb49
-rw-r--r--app/serializers/rest/admin/webhook_event_serializer.rb2
-rw-r--r--app/services/notify_service.rb13
-rw-r--r--app/services/update_status_service.rb4
-rw-r--r--app/views/kaminari/_gap.html.haml9
-rw-r--r--app/views/settings/login_activities/_login_activity.html.haml2
-rw-r--r--app/views/settings/preferences/appearance/show.html.haml71
-rw-r--r--app/views/settings/preferences/notifications/show.html.haml40
-rw-r--r--app/views/settings/preferences/other/show.html.haml35
-rw-r--r--app/workers/poll_expiration_notify_worker.rb2
-rw-r--r--app/workers/scheduler/indexing_scheduler.rb16
166 files changed, 1356 insertions, 1467 deletions
diff --git a/app/controllers/api/v1/accounts/credentials_controller.rb b/app/controllers/api/v1/accounts/credentials_controller.rb
index 94b707771..7c7d70fd3 100644
--- a/app/controllers/api/v1/accounts/credentials_controller.rb
+++ b/app/controllers/api/v1/accounts/credentials_controller.rb
@@ -13,7 +13,7 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
   def update
     @account = current_account
     UpdateAccountService.new.call(@account, account_params, raise_error: true)
-    UserSettingsDecorator.new(current_user).update(user_settings_params) if user_settings_params
+    current_user.update(user_params) if user_params
     ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
     render json: @account, serializer: REST::CredentialAccountSerializer
   end
@@ -34,15 +34,17 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
     )
   end
 
-  def user_settings_params
+  def user_params
     return nil if params[:source].blank?
 
     source_params = params.require(:source)
 
     {
-      'setting_default_privacy' => source_params.fetch(:privacy, @account.user.setting_default_privacy),
-      'setting_default_sensitive' => source_params.fetch(:sensitive, @account.user.setting_default_sensitive),
-      'setting_default_language' => source_params.fetch(:language, @account.user.setting_default_language),
+      settings_attributes: {
+        default_privacy: source_params.fetch(:privacy, @account.user.setting_default_privacy),
+        default_sensitive: source_params.fetch(:sensitive, @account.user.setting_default_sensitive),
+        default_language: source_params.fetch(:language, @account.user.setting_default_language),
+      },
     }
   end
 end
diff --git a/app/controllers/api/v1/streaming_controller.rb b/app/controllers/api/v1/streaming_controller.rb
index b23a60170..0cdd00d62 100644
--- a/app/controllers/api/v1/streaming_controller.rb
+++ b/app/controllers/api/v1/streaming_controller.rb
@@ -5,7 +5,7 @@ class Api::V1::StreamingController < Api::BaseController
     if Rails.configuration.x.streaming_api_base_url == request.host
       not_found
     else
-      redirect_to streaming_api_url, status: 301
+      redirect_to streaming_api_url, status: 301, allow_other_host: true
     end
   end
 
diff --git a/app/controllers/backups_controller.rb b/app/controllers/backups_controller.rb
index 2f4b400b8..0687b62c5 100644
--- a/app/controllers/backups_controller.rb
+++ b/app/controllers/backups_controller.rb
@@ -13,7 +13,11 @@ class BackupsController < ApplicationController
     when :s3
       redirect_to @backup.dump.expiring_url(10)
     when :fog
-      redirect_to @backup.dump.expiring_url(Time.now.utc + 10)
+      if Paperclip::Attachment.default_options.dig(:storage, :fog_credentials, :openstack_temp_url_key).present?
+        redirect_to @backup.dump.expiring_url(Time.now.utc + 10)
+      else
+        redirect_to full_asset_url(@backup.dump.url)
+      end
     when :filesystem
       redirect_to full_asset_url(@backup.dump.url)
     end
diff --git a/app/controllers/concerns/account_controller_concern.rb b/app/controllers/concerns/account_controller_concern.rb
index 2f7d84df0..e9cff22ca 100644
--- a/app/controllers/concerns/account_controller_concern.rb
+++ b/app/controllers/concerns/account_controller_concern.rb
@@ -10,7 +10,8 @@ module AccountControllerConcern
 
   included do
     before_action :set_instance_presenter
-    before_action :set_link_headers, if: -> { request.format.nil? || request.format == :html }
+
+    after_action :set_link_headers, if: -> { request.format.nil? || request.format == :html }
   end
 
   private
diff --git a/app/controllers/media_proxy_controller.rb b/app/controllers/media_proxy_controller.rb
index 3b228722f..f29b69a24 100644
--- a/app/controllers/media_proxy_controller.rb
+++ b/app/controllers/media_proxy_controller.rb
@@ -23,7 +23,7 @@ class MediaProxyController < ApplicationController
       redownload! if @media_attachment.needs_redownload? && !reject_media?
     end
 
-    redirect_to full_asset_url(@media_attachment.file.url(version))
+    redirect_to full_asset_url(@media_attachment.file.url(version)), allow_other_host: true
   end
 
   private
diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb
index 4c1336436..281deb64d 100644
--- a/app/controllers/settings/preferences_controller.rb
+++ b/app/controllers/settings/preferences_controller.rb
@@ -4,8 +4,6 @@ class Settings::PreferencesController < Settings::BaseController
   def show; end
 
   def update
-    user_settings.update(user_settings_params.to_h)
-
     if current_user.update(user_params)
       I18n.locale = current_user.locale
       redirect_to after_update_redirect_path, notice: I18n.t('generic.changes_saved_msg')
@@ -20,46 +18,7 @@ class Settings::PreferencesController < Settings::BaseController
     settings_preferences_path
   end
 
-  def user_settings
-    UserSettingsDecorator.new(current_user)
-  end
-
   def user_params
-    params.require(:user).permit(
-      :locale,
-      chosen_languages: []
-    )
-  end
-
-  def user_settings_params
-    params.require(:user).permit(
-      :setting_default_privacy,
-      :setting_default_sensitive,
-      :setting_default_language,
-      :setting_unfollow_modal,
-      :setting_boost_modal,
-      :setting_favourite_modal,
-      :setting_delete_modal,
-      :setting_auto_play_gif,
-      :setting_display_media,
-      :setting_expand_spoilers,
-      :setting_reduce_motion,
-      :setting_disable_swiping,
-      :setting_system_font_ui,
-      :setting_system_emoji_font,
-      :setting_noindex,
-      :setting_hide_followers_count,
-      :setting_aggregate_reblogs,
-      :setting_show_application,
-      :setting_advanced_layout,
-      :setting_default_content_type,
-      :setting_use_blurhash,
-      :setting_use_pending_items,
-      :setting_trends,
-      :setting_crop_images,
-      :setting_always_send_emails,
-      notification_emails: %i(follow follow_request reblog favourite mention report pending_account trending_tag trending_link trending_status appeal),
-      interactions: %i(must_be_follower must_be_following must_be_following_dm)
-    )
+    params.require(:user).permit(:locale, chosen_languages: [], settings_attributes: UserSettings.keys)
   end
 end
diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb
index e5221df3a..15c081264 100644
--- a/app/controllers/statuses_controller.rb
+++ b/app/controllers/statuses_controller.rb
@@ -9,11 +9,12 @@ class StatusesController < ApplicationController
   before_action :require_account_signature!, only: [:show, :activity], if: -> { request.format == :json && authorized_fetch_mode? }
   before_action :set_status
   before_action :set_instance_presenter
-  before_action :set_link_headers
   before_action :redirect_to_original, only: :show
   before_action :set_cache_headers
   before_action :set_body_classes, only: :embed
 
+  after_action :set_link_headers
+
   skip_around_action :set_locale, if: -> { request.format == :json }
   skip_before_action :require_functional!, only: [:show, :embed], unless: :whitelist_mode?
 
@@ -71,6 +72,6 @@ class StatusesController < ApplicationController
   end
 
   def redirect_to_original
-    redirect_to ActivityPub::TagManager.instance.url_for(@status.reblog) if @status.reblog?
+    redirect_to(ActivityPub::TagManager.instance.url_for(@status.reblog), allow_other_host: true) if @status.reblog?
   end
 end
diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js
index ef08edbf8..e1db44359 100644
--- a/app/javascript/mastodon/actions/compose.js
+++ b/app/javascript/mastodon/actions/compose.js
@@ -4,7 +4,6 @@ import { defineMessages } from 'react-intl';
 import api from 'mastodon/api';
 import { search as emojiSearch } from 'mastodon/features/emoji/emoji_mart_search_light';
 import { tagHistory } from 'mastodon/settings';
-import resizeImage from 'mastodon/utils/resize_image';
 import { showAlert, showAlertForError } from './alerts';
 import { useEmoji } from './emojis';
 import { importFetchedAccounts, importFetchedStatus } from './importer';
@@ -276,46 +275,42 @@ export function uploadCompose(files) {
 
     dispatch(uploadComposeRequest());
 
-    for (const [i, f] of Array.from(files).entries()) {
+    for (const [i, file] of Array.from(files).entries()) {
       if (media.size + i > 3) break;
 
-      resizeImage(f).then(file => {
-        const data = new FormData();
-        data.append('file', file);
-        // Account for disparity in size of original image and resized data
-        total += file.size - f.size;
-
-        return api(getState).post('/api/v2/media', data, {
-          onUploadProgress: function({ loaded }){
-            progress[i] = loaded;
-            dispatch(uploadComposeProgress(progress.reduce((a, v) => a + v, 0), total));
-          },
-        }).then(({ status, data }) => {
-          // If server-side processing of the media attachment has not completed yet,
-          // poll the server until it is, before showing the media attachment as uploaded
-
-          if (status === 200) {
-            dispatch(uploadComposeSuccess(data, f));
-          } else if (status === 202) {
-            dispatch(uploadComposeProcessing());
-
-            let tryCount = 1;
-
-            const poll = () => {
-              api(getState).get(`/api/v1/media/${data.id}`).then(response => {
-                if (response.status === 200) {
-                  dispatch(uploadComposeSuccess(response.data, f));
-                } else if (response.status === 206) {
-                  const retryAfter = (Math.log2(tryCount) || 1) * 1000;
-                  tryCount += 1;
-                  setTimeout(() => poll(), retryAfter);
-                }
-              }).catch(error => dispatch(uploadComposeFail(error)));
-            };
-
-            poll();
-          }
-        });
+      const data = new FormData();
+      data.append('file', file);
+
+      api(getState).post('/api/v2/media', data, {
+        onUploadProgress: function({ loaded }){
+          progress[i] = loaded;
+          dispatch(uploadComposeProgress(progress.reduce((a, v) => a + v, 0), total));
+        },
+      }).then(({ status, data }) => {
+        // If server-side processing of the media attachment has not completed yet,
+        // poll the server until it is, before showing the media attachment as uploaded
+
+        if (status === 200) {
+          dispatch(uploadComposeSuccess(data, file));
+        } else if (status === 202) {
+          dispatch(uploadComposeProcessing());
+
+          let tryCount = 1;
+
+          const poll = () => {
+            api(getState).get(`/api/v1/media/${data.id}`).then(response => {
+              if (response.status === 200) {
+                dispatch(uploadComposeSuccess(response.data, file));
+              } else if (response.status === 206) {
+                const retryAfter = (Math.log2(tryCount) || 1) * 1000;
+                tryCount += 1;
+                setTimeout(() => poll(), retryAfter);
+              }
+            }).catch(error => dispatch(uploadComposeFail(error)));
+          };
+
+          poll();
+        }
       }).catch(error => dispatch(uploadComposeFail(error)));
     }
   };
diff --git a/app/javascript/mastodon/components/account.jsx b/app/javascript/mastodon/components/account.jsx
index 7aaa668fe..0ee0e4551 100644
--- a/app/javascript/mastodon/components/account.jsx
+++ b/app/javascript/mastodon/components/account.jsx
@@ -10,6 +10,9 @@ import { me } from '../initial_state';
 import RelativeTimestamp from './relative_timestamp';
 import Skeleton from 'mastodon/components/skeleton';
 import { Link } from 'react-router-dom';
+import { counterRenderer } from 'mastodon/components/common_counter';
+import ShortNumber from 'mastodon/components/short_number';
+import Icon from 'mastodon/components/icon';
 
 const messages = defineMessages({
   follow: { id: 'account.follow', defaultMessage: 'Follow' },
@@ -23,6 +26,26 @@ const messages = defineMessages({
   block: { id: 'account.block', defaultMessage: 'Block @{name}' },
 });
 
+class VerifiedBadge extends React.PureComponent {
+
+  static propTypes = {
+    link: PropTypes.string.isRequired,
+    verifiedAt: PropTypes.string.isRequired,
+  };
+
+  render () {
+    const { link } = this.props;
+
+    return (
+      <span className='verified-badge'>
+        <Icon id='check' className='verified-badge__mark' />
+        <span dangerouslySetInnerHTML={{ __html: link }} />
+      </span>
+    );
+  }
+
+}
+
 class Account extends ImmutablePureComponent {
 
   static propTypes = {
@@ -77,7 +100,11 @@ class Account extends ImmutablePureComponent {
           <div className='account__wrapper'>
             <div className='account__display-name'>
               <div className='account__avatar-wrapper'><Skeleton width={36} height={36} /></div>
-              <DisplayName />
+
+              <div>
+                <DisplayName />
+                <Skeleton width='7ch' />
+              </div>
             </div>
           </div>
         </div>
@@ -131,18 +158,32 @@ class Account extends ImmutablePureComponent {
       }
     }
 
-    let mute_expires_at;
+    let muteTimeRemaining;
+
     if (account.get('mute_expires_at')) {
-      mute_expires_at =  <div><RelativeTimestamp timestamp={account.get('mute_expires_at')} futureDate /></div>;
+      muteTimeRemaining = <>· <RelativeTimestamp timestamp={account.get('mute_expires_at')} futureDate /></>;
+    }
+
+    let verification;
+
+    const firstVerifiedField = account.get('fields').find(item => !!item.get('verified_at'));
+
+    if (firstVerifiedField) {
+      verification = <>· <VerifiedBadge link={firstVerifiedField.get('value')} verifiedAt={firstVerifiedField.get('verified_at')} /></>;
     }
 
     return (
       <div className='account'>
         <div className='account__wrapper'>
           <Link key={account.get('id')} className='account__display-name' title={account.get('acct')} to={`/@${account.get('acct')}`}>
-            <div className='account__avatar-wrapper'><Avatar account={account} size={size} /></div>
-            {mute_expires_at}
-            <DisplayName account={account} />
+            <div className='account__avatar-wrapper'>
+              <Avatar account={account} size={size} />
+            </div>
+
+            <div>
+              <DisplayName account={account} />
+              <ShortNumber value={account.get('followers_count')} renderer={counterRenderer('followers')} /> {verification} {muteTimeRemaining}
+            </div>
           </Link>
 
           <div className='account__relationship'>
diff --git a/app/javascript/mastodon/components/status.jsx b/app/javascript/mastodon/components/status.jsx
index 2d200a55b..281e69874 100644
--- a/app/javascript/mastodon/components/status.jsx
+++ b/app/javascript/mastodon/components/status.jsx
@@ -386,6 +386,13 @@ class Status extends ImmutablePureComponent {
 
       account = status.get('account');
       status  = status.get('reblog');
+    } else if (status.get('visibility') === 'direct') {
+      prepend = (
+        <div className='status__prepend'>
+          <div className='status__prepend-icon-wrapper'><Icon id='at' className='status__prepend-icon' fixedWidth /></div>
+          <FormattedMessage id='status.direct_indicator' defaultMessage='Private mention' />
+        </div>
+      );
     } else if (showThread && status.get('in_reply_to_id') && status.get('in_reply_to_account_id') === status.getIn(['account', 'id'])) {
       const display_name_html = { __html: status.getIn(['account', 'display_name_html']) };
 
diff --git a/app/javascript/mastodon/components/status_action_bar.jsx b/app/javascript/mastodon/components/status_action_bar.jsx
index 08e37e089..7b4031b68 100644
--- a/app/javascript/mastodon/components/status_action_bar.jsx
+++ b/app/javascript/mastodon/components/status_action_bar.jsx
@@ -14,7 +14,7 @@ const messages = defineMessages({
   delete: { id: 'status.delete', defaultMessage: 'Delete' },
   redraft: { id: 'status.redraft', defaultMessage: 'Delete & re-draft' },
   edit: { id: 'status.edit', defaultMessage: 'Edit' },
-  direct: { id: 'status.direct', defaultMessage: 'Direct message @{name}' },
+  direct: { id: 'status.direct', defaultMessage: 'Privately mention @{name}' },
   mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
   mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
   block: { id: 'account.block', defaultMessage: 'Block @{name}' },
diff --git a/app/javascript/mastodon/features/account/components/header.jsx b/app/javascript/mastodon/features/account/components/header.jsx
index be6b17896..72eb7e6b6 100644
--- a/app/javascript/mastodon/features/account/components/header.jsx
+++ b/app/javascript/mastodon/features/account/components/header.jsx
@@ -28,7 +28,7 @@ const messages = defineMessages({
   linkVerifiedOn: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' },
   account_locked: { id: 'account.locked_info', defaultMessage: 'This account privacy status is set to locked. The owner manually reviews who can follow them.' },
   mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' },
-  direct: { id: 'account.direct', defaultMessage: 'Direct message @{name}' },
+  direct: { id: 'account.direct', defaultMessage: 'Privately mention @{name}' },
   unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
   block: { id: 'account.block', defaultMessage: 'Block @{name}' },
   mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
diff --git a/app/javascript/mastodon/features/direct_timeline/index.jsx b/app/javascript/mastodon/features/direct_timeline/index.jsx
index e2667d8e9..5d4a7c49a 100644
--- a/app/javascript/mastodon/features/direct_timeline/index.jsx
+++ b/app/javascript/mastodon/features/direct_timeline/index.jsx
@@ -11,7 +11,7 @@ import ColumnHeader from 'mastodon/components/column_header';
 import ConversationsListContainer from './containers/conversations_list_container';
 
 const messages = defineMessages({
-  title: { id: 'column.direct', defaultMessage: 'Direct messages' },
+  title: { id: 'column.direct', defaultMessage: 'Private mentions' },
 });
 
 class DirectTimeline extends React.PureComponent {
@@ -91,7 +91,7 @@ class DirectTimeline extends React.PureComponent {
           timelineId='direct'
           onLoadMore={this.handleLoadMore}
           prepend={<div className='follow_requests-unlocked_explanation'><span><FormattedMessage id='compose_form.encryption_warning' defaultMessage='Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.' /> <a href='/terms' target='_blank'><FormattedMessage id='compose_form.direct_message_warning_learn_more' defaultMessage='Learn more' /></a></span></div>}
-          emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any direct messages yet. When you send or receive one, it will show up here." />}
+          emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any private mentions yet. When you send or receive one, it will show up here." />}
         />
 
         <Helmet>
diff --git a/app/javascript/mastodon/features/explore/index.jsx b/app/javascript/mastodon/features/explore/index.jsx
index 569b8612c..939550d83 100644
--- a/app/javascript/mastodon/features/explore/index.jsx
+++ b/app/javascript/mastodon/features/explore/index.jsx
@@ -88,7 +88,9 @@ class Explore extends React.PureComponent {
                 <Route path='/explore/tags' component={Tags} />
                 <Route path='/explore/links' component={Links} />
                 <Route path='/explore/suggestions' component={Suggestions} />
-                <Route exact path={['/explore', '/explore/posts', '/search']} component={Statuses} componentParams={{ multiColumn }} />
+                <Route exact path={['/explore', '/explore/posts', '/search']}>
+                  <Statuses multiColumn={multiColumn} />
+                </Route>
               </Switch>
 
               <Helmet>
diff --git a/app/javascript/mastodon/features/getting_started/index.jsx b/app/javascript/mastodon/features/getting_started/index.jsx
index ee8a009ee..29659acc7 100644
--- a/app/javascript/mastodon/features/getting_started/index.jsx
+++ b/app/javascript/mastodon/features/getting_started/index.jsx
@@ -23,7 +23,7 @@ const messages = defineMessages({
   settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings' },
   community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
   explore: { id: 'navigation_bar.explore', defaultMessage: 'Explore' },
-  direct: { id: 'navigation_bar.direct', defaultMessage: 'Direct messages' },
+  direct: { id: 'navigation_bar.direct', defaultMessage: 'Private mentions' },
   bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
   preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
   follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
diff --git a/app/javascript/mastodon/features/status/components/action_bar.jsx b/app/javascript/mastodon/features/status/components/action_bar.jsx
index 5cbe4984c..0c74c4cc4 100644
--- a/app/javascript/mastodon/features/status/components/action_bar.jsx
+++ b/app/javascript/mastodon/features/status/components/action_bar.jsx
@@ -13,7 +13,7 @@ const messages = defineMessages({
   delete: { id: 'status.delete', defaultMessage: 'Delete' },
   redraft: { id: 'status.redraft', defaultMessage: 'Delete & re-draft' },
   edit: { id: 'status.edit', defaultMessage: 'Edit' },
-  direct: { id: 'status.direct', defaultMessage: 'Direct message @{name}' },
+  direct: { id: 'status.direct', defaultMessage: 'Privately mention @{name}' },
   mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
   reply: { id: 'status.reply', defaultMessage: 'Reply' },
   reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
diff --git a/app/javascript/mastodon/features/status/components/detailed_status.jsx b/app/javascript/mastodon/features/status/components/detailed_status.jsx
index 8a2194450..e4e572026 100644
--- a/app/javascript/mastodon/features/status/components/detailed_status.jsx
+++ b/app/javascript/mastodon/features/status/components/detailed_status.jsx
@@ -6,7 +6,7 @@ import DisplayName from '../../../components/display_name';
 import StatusContent from '../../../components/status_content';
 import MediaGallery from '../../../components/media_gallery';
 import { Link } from 'react-router-dom';
-import { injectIntl, defineMessages, FormattedDate } from 'react-intl';
+import { injectIntl, defineMessages, FormattedDate, FormattedMessage } from 'react-intl';
 import Card from './card';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import Video from '../../video';
@@ -262,7 +262,13 @@ class DetailedStatus extends ImmutablePureComponent {
 
     return (
       <div style={outerStyle}>
-        <div ref={this.setRef} className={classNames('detailed-status', `detailed-status-${status.get('visibility')}`, { compact })}>
+        <div ref={this.setRef} className={classNames('detailed-status', { compact })}>
+          {status.get('visibility') === 'direct' && (
+            <div className='status__prepend'>
+              <div className='status__prepend-icon-wrapper'><Icon id='at' className='status__prepend-icon' fixedWidth /></div>
+              <FormattedMessage id='status.direct_indicator' defaultMessage='Private mention' />
+            </div>
+          )}
           <a href={`/@${status.getIn(['account', 'acct'])}`} onClick={this.handleAccountClick} className='detailed-status__display-name'>
             <div className='detailed-status__display-avatar'><Avatar account={status.get('account')} size={46} /></div>
             <DisplayName account={status.get('account')} localDomain={this.props.domain} />
diff --git a/app/javascript/mastodon/features/status/index.jsx b/app/javascript/mastodon/features/status/index.jsx
index 358ad14c4..1d7cb685f 100644
--- a/app/javascript/mastodon/features/status/index.jsx
+++ b/app/javascript/mastodon/features/status/index.jsx
@@ -630,7 +630,7 @@ class Status extends ImmutablePureComponent {
             {ancestors}
 
             <HotKeys handlers={handlers}>
-              <div className={classNames('focusable', 'detailed-status__wrapper')} tabIndex='0' aria-label={textForScreenReader(intl, status, false)}>
+              <div className={classNames('focusable', 'detailed-status__wrapper', `detailed-status__wrapper-${status.get('visibility')}`)} tabIndex='0' aria-label={textForScreenReader(intl, status, false)}>
                 <DetailedStatus
                   key={`details-${status.get('id')}`}
                   status={status}
diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx
index 90a3fcdf7..ee1a83cc6 100644
--- a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx
+++ b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx
@@ -18,7 +18,7 @@ const messages = defineMessages({
   explore: { id: 'explore.title', defaultMessage: 'Explore' },
   local: { id: 'tabs_bar.local_timeline', defaultMessage: 'Local' },
   federated: { id: 'tabs_bar.federated_timeline', defaultMessage: 'Federated' },
-  direct: { id: 'navigation_bar.direct', defaultMessage: 'Direct messages' },
+  direct: { id: 'navigation_bar.direct', defaultMessage: 'Private mentions' },
   favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
   bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
   lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
diff --git a/app/javascript/mastodon/locales/af.json b/app/javascript/mastodon/locales/af.json
index c04cae948..59dbdfa63 100644
--- a/app/javascript/mastodon/locales/af.json
+++ b/app/javascript/mastodon/locales/af.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/an.json b/app/javascript/mastodon/locales/an.json
index 9f66c8d13..e1414a8f5 100644
--- a/app/javascript/mastodon/locales/an.json
+++ b/app/javascript/mastodon/locales/an.json
@@ -558,6 +558,7 @@
   "status.delete": "Borrar",
   "status.detailed_status": "Vista de conversación detallada",
   "status.direct": "Mensache directo a @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Editar",
   "status.edited": "Editau {date}",
   "status.edited_x_times": "Editau {count, plural, one {{count} vez} other {{count} veces}}",
diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index d7d3baf4e..4aec5838d 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -558,6 +558,7 @@
   "status.delete": "احذف",
   "status.detailed_status": "تفاصيل المحادثة",
   "status.direct": "رسالة خاصة إلى @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "تعديل",
   "status.edited": "عُدّل في {date}",
   "status.edited_x_times": "عُدّل {count, plural, zero {} one {مرةً واحدة} two {مرّتان} few {{count} مرات} many {{count} مرة} other {{count} مرة}}",
diff --git a/app/javascript/mastodon/locales/ast.json b/app/javascript/mastodon/locales/ast.json
index 2881911a6..9e356881a 100644
--- a/app/javascript/mastodon/locales/ast.json
+++ b/app/javascript/mastodon/locales/ast.json
@@ -558,6 +558,7 @@
   "status.delete": "Desaniciar",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Unviar un mensaxe direutu a @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Editóse'l {date}",
   "status.edited_x_times": "Editóse {count, plural, one {{count} vegada} other {{count} vegaes}}",
diff --git a/app/javascript/mastodon/locales/be.json b/app/javascript/mastodon/locales/be.json
index 05c328f6a..ce51ee507 100644
--- a/app/javascript/mastodon/locales/be.json
+++ b/app/javascript/mastodon/locales/be.json
@@ -163,7 +163,7 @@
   "confirmations.domain_block.confirm": "Заблакіраваць дамен цалкам",
   "confirmations.domain_block.message": "Вы абсалютна дакладна ўпэўнены, што хочаце заблакіраваць {domain} зусім? У большасці выпадкаў, дастаткова некалькіх мэтавых блакіровак ці ігнараванняў. Вы перастанеце бачыць змесціва з гэтага дамену ва ўсіх стужках і апавяшчэннях. Вашы падпіскі з гэтага дамену будуць выдаленыя.",
   "confirmations.edit.confirm": "Рэдагаваць",
-  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
+  "confirmations.edit.message": "Калі вы зменіце зараз, гэта ператрэ паведамленне, якое вы пішаце. Вы ўпэўнены, што хочаце працягнуць?",
   "confirmations.logout.confirm": "Выйсці",
   "confirmations.logout.message": "Вы ўпэўненыя, што хочаце выйсці?",
   "confirmations.mute.confirm": "Ігнараваць",
@@ -558,6 +558,7 @@
   "status.delete": "Выдаліць",
   "status.detailed_status": "Дэтальны агляд размовы",
   "status.direct": "Асабістае паведамленне @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Рэдагаваць",
   "status.edited": "Адрэдагавана {date}",
   "status.edited_x_times": "Рэдагавана {count, plural, one {{count} раз} few {{count} разы} many {{count} разоў} other {{count} разу}}",
diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json
index 248fd1ccb..6b6bd1011 100644
--- a/app/javascript/mastodon/locales/bg.json
+++ b/app/javascript/mastodon/locales/bg.json
@@ -558,6 +558,7 @@
   "status.delete": "Изтриване",
   "status.detailed_status": "Подробен изглед на разговора",
   "status.direct": "Директно съобщение до @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Редактиране",
   "status.edited": "Редактирано на {date}",
   "status.edited_x_times": "Редактирано {count, plural,one {{count} път} other {{count} пъти}}",
diff --git a/app/javascript/mastodon/locales/bn.json b/app/javascript/mastodon/locales/bn.json
index 7da6f78dc..8dbac2d58 100644
--- a/app/javascript/mastodon/locales/bn.json
+++ b/app/javascript/mastodon/locales/bn.json
@@ -558,6 +558,7 @@
   "status.delete": "মুছে ফেলতে",
   "status.detailed_status": "বিস্তারিত কথোপকথনের হিসেবে দেখতে",
   "status.direct": "@{name} কে সরাসরি লেখা পাঠাতে",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/br.json b/app/javascript/mastodon/locales/br.json
index 5cc331af4..0884047ac 100644
--- a/app/javascript/mastodon/locales/br.json
+++ b/app/javascript/mastodon/locales/br.json
@@ -558,6 +558,7 @@
   "status.delete": "Dilemel",
   "status.detailed_status": "Gwel kaozeadenn munudek",
   "status.direct": "Kas ur c'hannad eeun da @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Kemmañ",
   "status.edited": "Aozet {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/bs.json b/app/javascript/mastodon/locales/bs.json
index d67f73c00..e4dda7c77 100644
--- a/app/javascript/mastodon/locales/bs.json
+++ b/app/javascript/mastodon/locales/bs.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index ebfafcbd3..a65a41111 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -558,6 +558,7 @@
   "status.delete": "Elimina",
   "status.detailed_status": "Vista detallada de la conversa",
   "status.direct": "Missatge directe a @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edita",
   "status.edited": "Editat {date}",
   "status.edited_x_times": "Editat {count, plural, one {{count} vegada} other {{count} vegades}}",
diff --git a/app/javascript/mastodon/locales/ckb.json b/app/javascript/mastodon/locales/ckb.json
index 7623b5b99..095e73688 100644
--- a/app/javascript/mastodon/locales/ckb.json
+++ b/app/javascript/mastodon/locales/ckb.json
@@ -558,6 +558,7 @@
   "status.delete": "سڕینەوە",
   "status.detailed_status": "ڕوانگەی گفتوگۆ بە وردەکاری",
   "status.direct": "پەیامی ڕاستەوخۆ @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "دەستکاری",
   "status.edited": "بەشداری {date}",
   "status.edited_x_times": "دەستکاریکراوە {count, plural, one {{count} کات} other {{count} کات}}",
diff --git a/app/javascript/mastodon/locales/co.json b/app/javascript/mastodon/locales/co.json
index 2ede2311e..dfa8bcd33 100644
--- a/app/javascript/mastodon/locales/co.json
+++ b/app/javascript/mastodon/locales/co.json
@@ -558,6 +558,7 @@
   "status.delete": "Toglie",
   "status.detailed_status": "Vista in ditagliu di a cunversazione",
   "status.direct": "Mandà un missaghju @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json
index 97bc50c86..392312650 100644
--- a/app/javascript/mastodon/locales/cs.json
+++ b/app/javascript/mastodon/locales/cs.json
@@ -558,6 +558,7 @@
   "status.delete": "Smazat",
   "status.detailed_status": "Podrobné zobrazení konverzace",
   "status.direct": "Poslat @{name} přímou zprávu",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Upravit",
   "status.edited": "Upraveno {date}",
   "status.edited_x_times": "Upraveno {count, plural, one {{count}krát} few {{count}krát} many {{count}krát} other {{count}krát}}",
diff --git a/app/javascript/mastodon/locales/csb.json b/app/javascript/mastodon/locales/csb.json
index 03c5af685..b5078462f 100644
--- a/app/javascript/mastodon/locales/csb.json
+++ b/app/javascript/mastodon/locales/csb.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json
index 21c0cd2be..ace8185d9 100644
--- a/app/javascript/mastodon/locales/cy.json
+++ b/app/javascript/mastodon/locales/cy.json
@@ -558,6 +558,7 @@
   "status.delete": "Dileu",
   "status.detailed_status": "Golwg manwl o'r sgwrs",
   "status.direct": "Neges breifat @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Golygu",
   "status.edited": "Golygwyd {date}",
   "status.edited_x_times": "Golygwyd {count, plural, one {waith} two {waith} other {{count} gwaith}}",
diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json
index 6caa763f0..97fb90a8f 100644
--- a/app/javascript/mastodon/locales/da.json
+++ b/app/javascript/mastodon/locales/da.json
@@ -558,6 +558,7 @@
   "status.delete": "Slet",
   "status.detailed_status": "Detaljeret samtalevisning",
   "status.direct": "Direkte besked til @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Redigér",
   "status.edited": "Redigeret {date}",
   "status.edited_x_times": "Redigeret {count, plural, one {{count} gang} other {{count} gange}}",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index dfc478ae5..24cec0228 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -558,6 +558,7 @@
   "status.delete": "Beitrag löschen",
   "status.detailed_status": "Detaillierte Ansicht der Unterhaltung",
   "status.direct": "Direktnachricht an @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Beitrag bearbeiten",
   "status.edited": "Bearbeitet {date}",
   "status.edited_x_times": "{count, plural, one {{count} mal} other {{count} mal}} bearbeitet",
diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json
index 536566945..d8f40aa51 100644
--- a/app/javascript/mastodon/locales/defaultMessages.json
+++ b/app/javascript/mastodon/locales/defaultMessages.json
@@ -560,7 +560,7 @@
         "id": "status.edit"
       },
       {
-        "defaultMessage": "Direct message @{name}",
+        "defaultMessage": "Privately mention @{name}",
         "id": "status.direct"
       },
       {
@@ -762,6 +762,10 @@
         "id": "status.reblogged_by"
       },
       {
+        "defaultMessage": "Private mention",
+        "id": "status.direct_indicator"
+      },
+      {
         "defaultMessage": "Replied to {name}",
         "id": "status.replied_to"
       }
@@ -1109,7 +1113,7 @@
         "id": "account.mention"
       },
       {
-        "defaultMessage": "Direct message @{name}",
+        "defaultMessage": "Privately mention @{name}",
         "id": "account.direct"
       },
       {
@@ -1627,7 +1631,7 @@
         "id": "privacy.private.long"
       },
       {
-        "defaultMessage": "Mentioned people only",
+        "defaultMessage": "Private mention",
         "id": "privacy.direct.short"
       },
       {
@@ -1925,7 +1929,7 @@
   {
     "descriptors": [
       {
-        "defaultMessage": "Direct messages",
+        "defaultMessage": "Private mentions",
         "id": "column.direct"
       },
       {
@@ -1937,7 +1941,7 @@
         "id": "compose_form.direct_message_warning_learn_more"
       },
       {
-        "defaultMessage": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+        "defaultMessage": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
         "id": "empty_column.direct"
       }
     ],
@@ -2432,7 +2436,7 @@
         "id": "navigation_bar.explore"
       },
       {
-        "defaultMessage": "Direct messages",
+        "defaultMessage": "Private mentions",
         "id": "navigation_bar.direct"
       },
       {
@@ -3550,7 +3554,7 @@
         "id": "status.edit"
       },
       {
-        "defaultMessage": "Direct message @{name}",
+        "defaultMessage": "Privately mention @{name}",
         "id": "status.direct"
       },
       {
@@ -4199,7 +4203,7 @@
         "id": "tabs_bar.federated_timeline"
       },
       {
-        "defaultMessage": "Direct messages",
+        "defaultMessage": "Private mentions",
         "id": "navigation_bar.direct"
       },
       {
diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json
index 0813a9857..e972494ad 100644
--- a/app/javascript/mastodon/locales/el.json
+++ b/app/javascript/mastodon/locales/el.json
@@ -558,6 +558,7 @@
   "status.delete": "Διαγραφή",
   "status.detailed_status": "Προβολή λεπτομερειών συζήτησης",
   "status.direct": "Προσωπικό μήνυμα προς @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Επεξεργασία",
   "status.edited": "Επεξεργάστηκε στις {date}",
   "status.edited_x_times": "Επεξεργάστηκε {count, plural, one {{count} φορά} other {{count} φορές}}",
diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json
index 974c6dd84..359cc0388 100644
--- a/app/javascript/mastodon/locales/en-GB.json
+++ b/app/javascript/mastodon/locales/en-GB.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index 3a515d4d7..a83c0f37d 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blocked",
   "account.browse_more_on_origin_server": "Browse more on the original profile",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Direct message @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Stop notifying me when @{name} posts",
   "account.domain_blocked": "Domain blocked",
   "account.edit_profile": "Edit profile",
@@ -102,7 +102,7 @@
   "column.blocks": "Blocked users",
   "column.bookmarks": "Bookmarks",
   "column.community": "Local timeline",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Browse profiles",
   "column.domain_blocks": "Blocked domains",
   "column.favourites": "Favourites",
@@ -220,7 +220,7 @@
   "empty_column.blocks": "You haven't blocked any users yet.",
   "empty_column.bookmarked_statuses": "You don't have any bookmarked posts yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no blocked domains yet.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "You don't have any favourite posts yet. When you favourite one, it will show up here.",
@@ -318,7 +318,7 @@
   "keyboard_shortcuts.column": "Focus column",
   "keyboard_shortcuts.compose": "Focus compose textarea",
   "keyboard_shortcuts.description": "Description",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "to open private mentions column",
   "keyboard_shortcuts.down": "Move down in the list",
   "keyboard_shortcuts.enter": "Open post",
   "keyboard_shortcuts.favourite": "Favourite post",
@@ -380,7 +380,7 @@
   "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new post",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Blocked domains",
   "navigation_bar.edit_profile": "Edit profile",
@@ -562,7 +562,8 @@
   "status.copy": "Copy link to post",
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index 93b668d35..29ecea294 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -558,6 +558,7 @@
   "status.delete": "Forigi",
   "status.detailed_status": "Detala konversacia vido",
   "status.direct": "Rekte mesaĝi @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Redakti",
   "status.edited": "Redaktita {date}",
   "status.edited_x_times": "Redactita {count, plural, one {{count} fojon} other {{count} fojojn}}",
diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json
index 93238efbc..f9d6b21d9 100644
--- a/app/javascript/mastodon/locales/es-AR.json
+++ b/app/javascript/mastodon/locales/es-AR.json
@@ -558,6 +558,7 @@
   "status.delete": "Eliminar",
   "status.detailed_status": "Vista de conversación detallada",
   "status.direct": "Mensaje directo para @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Editar",
   "status.edited": "Editado {date}",
   "status.edited_x_times": "Editado {count, plural, one {{count} vez} other {{count} veces}}",
diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json
index 56e891611..d10dfa9b3 100644
--- a/app/javascript/mastodon/locales/es-MX.json
+++ b/app/javascript/mastodon/locales/es-MX.json
@@ -558,6 +558,7 @@
   "status.delete": "Borrar",
   "status.detailed_status": "Vista de conversación detallada",
   "status.direct": "Enviar mensaje a @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Editar",
   "status.edited": "Editado {date}",
   "status.edited_x_times": "Editado {count, plural, one {{count} time} other {{count} veces}}",
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
index 61e831526..ffdecda88 100644
--- a/app/javascript/mastodon/locales/es.json
+++ b/app/javascript/mastodon/locales/es.json
@@ -558,6 +558,7 @@
   "status.delete": "Borrar",
   "status.detailed_status": "Vista de conversación detallada",
   "status.direct": "Mensaje directo a @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Editar",
   "status.edited": "Editado {date}",
   "status.edited_x_times": "Editado {count, plural, one {{count} vez} other {{count} veces}}",
diff --git a/app/javascript/mastodon/locales/et.json b/app/javascript/mastodon/locales/et.json
index 9b8c83563..e8874a556 100644
--- a/app/javascript/mastodon/locales/et.json
+++ b/app/javascript/mastodon/locales/et.json
@@ -491,7 +491,7 @@
   "report.category.title_status": "postitusega",
   "report.close": "Valmis",
   "report.comment.title": "Kas arvad, et on veel midagi, mida me peaks teadma?",
-  "report.forward": "Edasta kasutajale {target}",
+  "report.forward": "Edasta ka {target} domeeni",
   "report.forward_hint": "See kasutaja on teisest serverist. Kas saadan anonümiseeritud koopia sellest teatest sinna ka?",
   "report.mute": "Vaigista",
   "report.mute_explanation": "Sa ei näe tema postitusi. Ta võib ikka sind jälgida ja su postitusi näha. Ta ei saa teada, et ta on vaigistatud.",
@@ -558,6 +558,7 @@
   "status.delete": "Kustuta",
   "status.detailed_status": "Detailne vestluskuva",
   "status.direct": "Saada otsesõnum @{name}'ile",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Muuda",
   "status.edited": "{date} muudetud",
   "status.edited_x_times": "Muudetud {count, plural, one{{count} kord} other {{count} korda}}",
diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json
index e5f2116bc..bf3f3e0e3 100644
--- a/app/javascript/mastodon/locales/eu.json
+++ b/app/javascript/mastodon/locales/eu.json
@@ -558,6 +558,7 @@
   "status.delete": "Ezabatu",
   "status.detailed_status": "Elkarrizketaren ikuspegi xehetsua",
   "status.direct": "Mezu zuzena @{name}(r)i",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Editatu",
   "status.edited": "Editatua {date}",
   "status.edited_x_times": "{count, plural, one {behin} other {{count} aldiz}} editatua",
diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json
index acb7525b2..a3eca3103 100644
--- a/app/javascript/mastodon/locales/fa.json
+++ b/app/javascript/mastodon/locales/fa.json
@@ -558,6 +558,7 @@
   "status.delete": "حذف",
   "status.detailed_status": "نمایش کامل گفتگو",
   "status.direct": "پیام مستقیم به ‎@{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "ویرایش",
   "status.edited": "ویرایش شده در {date}",
   "status.edited_x_times": "{count, plural, one {{count} مرتبه} other {{count} مرتبه}} ویرایش شد",
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index 8cff515bd..3d9506513 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -1,6 +1,6 @@
 {
   "about.blocks": "Moderoidut palvelimet",
-  "about.contact": "Yhteystiedot:",
+  "about.contact": "Ota yhteyttä:",
   "about.disclaimer": "Mastodon on vapaa avoimen lähdekoodin ohjelmisto ja Mastodon gGmbH:n tavaramerkki.",
   "about.domain_blocks.no_reason_available": "Syytä ei ole ilmoitettu",
   "about.domain_blocks.preamble": "Yleisesti Mastodonin avulla voidaan tarkastella minkä tahansa muun fediverse-palvelinten sisältöä ja vuorovaikuttaa eri palvelinten käyttäjien kanssa. Nämä ovat tälle palvelimelle määritetyt poikkeukset.",
@@ -9,7 +9,7 @@
   "about.domain_blocks.suspended.explanation": "Mitään tämän palvelimen tietoja ei käsitellä, tallenneta tai vaihdeta, mikä tekee vuorovaikutuksesta ja viestinnästä sen käyttäjien kanssa mahdotonta.",
   "about.domain_blocks.suspended.title": "Jäädytetty",
   "about.not_available": "Näitä tietoja ei ole julkaistu tällä palvelimella.",
-  "about.powered_by": "Hajautettu sosiaalinen media, tarjoaa {mastodon}",
+  "about.powered_by": "Hajautetun sosiaalisen median tarjoaa {mastodon}",
   "about.rules": "Palvelimen säännöt",
   "account.account_note_header": "Muistiinpano",
   "account.add_or_remove_from_list": "Lisää tai poista listoilta",
@@ -21,8 +21,8 @@
   "account.browse_more_on_origin_server": "Selaile lisää alkuperäisellä palvelimella",
   "account.cancel_follow_request": "Peruuta seurantapyyntö",
   "account.direct": "Yksityisviesti käyttäjälle @{name}",
-  "account.disable_notifications": "Lopeta @{name}:n julkaisuista ilmoittaminen",
-  "account.domain_blocked": "Verkko-osoite estetty",
+  "account.disable_notifications": "Lopeta ilmoittamasta minulle, kun @{name} julkaisee",
+  "account.domain_blocked": "Palvelu estetty",
   "account.edit_profile": "Muokkaa profiilia",
   "account.enable_notifications": "Ilmoita kun käyttäjä @{name} julkaisee viestin",
   "account.endorse": "Suosittele profiilissasi",
@@ -50,7 +50,7 @@
   "account.mute_notifications": "Mykistä käyttäjän @{name} ilmoitukset",
   "account.muted": "Mykistetty",
   "account.open_original_page": "Avaa alkuperäinen sivu",
-  "account.posts": "Viestit",
+  "account.posts": "Julkaisut",
   "account.posts_with_replies": "Viestit ja vastaukset",
   "account.report": "Ilmoita käyttäjästä @{name}",
   "account.requested": "Odottaa hyväksyntää. Peruuta seuraamispyyntö klikkaamalla",
@@ -58,7 +58,7 @@
   "account.share": "Jaa käyttäjän @{name} profiili",
   "account.show_reblogs": "Näytä tehostukset käyttäjältä @{name}",
   "account.statuses_counter": "{count, plural, one {{counter} viesti} other {{counter} viestiä}}",
-  "account.unblock": "Salli @{name}",
+  "account.unblock": "Poista esto: @{name}",
   "account.unblock_domain": "Salli palvelu {domain}",
   "account.unblock_short": "Poista esto",
   "account.unendorse": "Poista suosittelu profiilistasi",
@@ -104,7 +104,7 @@
   "column.community": "Paikallinen aikajana",
   "column.direct": "Yksityisviestit",
   "column.directory": "Selaa profiileja",
-  "column.domain_blocks": "Estetytr verkkotunnukset",
+  "column.domain_blocks": "Estetyt palvelut",
   "column.favourites": "Suosikit",
   "column.follow_requests": "Seuraamispyynnöt",
   "column.home": "Koti",
@@ -149,7 +149,7 @@
   "compose_form.spoiler.unmarked": "Lisää sisältövaroitus",
   "compose_form.spoiler_placeholder": "Kirjoita varoituksesi tähän",
   "confirmation_modal.cancel": "Peruuta",
-  "confirmations.block.block_and_report": "Estä ja raportoi",
+  "confirmations.block.block_and_report": "Estä ja ilmianna",
   "confirmations.block.confirm": "Estä",
   "confirmations.block.message": "Haluatko varmasti estää käyttäjän {name}?",
   "confirmations.cancel_follow_request.confirm": "Peruuta pyyntö",
@@ -163,7 +163,7 @@
   "confirmations.domain_block.confirm": "Estä koko palvelu",
   "confirmations.domain_block.message": "Haluatko aivan varmasti estää palvelun {domain} täysin? Useimmiten muutama kohdistettu esto tai mykistys on riittävä ja suositeltava toimenpide. Et näe kyseisen sisältöä kyseiseltä verkkoalueelta missään julkisissa aikajanoissa tai ilmoituksissa. Tälle verkkoalueelle kuuluvat seuraajasi poistetaan.",
   "confirmations.edit.confirm": "Muokkaa",
-  "confirmations.edit.message": "Tässä tilanteessa muokkaus korvaa tällä hetkellä työstämäsi viestin. Haluatko varmasti jatkaa?",
+  "confirmations.edit.message": "Muokkaaminen nyt korvaa viestin, jota paraikaa työstät. Haluatko varmasti jatkaa?",
   "confirmations.logout.confirm": "Kirjaudu ulos",
   "confirmations.logout.message": "Haluatko varmasti kirjautua ulos?",
   "confirmations.mute.confirm": "Mykistä",
@@ -210,15 +210,15 @@
   "emoji_button.search_results": "Hakutulokset",
   "emoji_button.symbols": "Symbolit",
   "emoji_button.travel": "Matkailu ja paikat",
-  "empty_column.account_suspended": "Tilin käyttäminen jäädytetty",
+  "empty_column.account_suspended": "Tili jäädytetty",
   "empty_column.account_timeline": "Ei viestejä täällä.",
   "empty_column.account_unavailable": "Profiilia ei löydy",
-  "empty_column.blocks": "Et ole vielä estänyt käyttäjiä.",
+  "empty_column.blocks": "Et ole estänyt käyttäjiä.",
   "empty_column.bookmarked_statuses": "Et ole vielä lisännyt viestejä kirjanmerkkeihisi. Kun lisäät yhden, se näkyy tässä.",
   "empty_column.community": "Paikallinen aikajana on tyhjä. Kirjoita jotain julkista, niin homma lähtee käyntiin!",
   "empty_column.direct": "Sinulla ei ole vielä yksityisviestejä. Kun lähetät tai vastaanotat sellaisen, se näkyy tässä.",
   "empty_column.domain_blocks": "Palveluita ei ole vielä estetty.",
-  "empty_column.explore_statuses": "Mikään ei ole nyt trendi. Tarkista myöhemmin!",
+  "empty_column.explore_statuses": "Mikään ei trendaa nyt. Tarkista myöhemmin uudelleen!",
   "empty_column.favourited_statuses": "Et ole vielä lisännyt viestejä kirjanmerkkeihisi. Kun lisäät yhden, se näkyy tässä.",
   "empty_column.favourites": "Kukaan ei ole vielä lisännyt tätä viestiä suosikkeihinsa. Kun joku tekee niin, näkyy kyseinen henkilö tässä.",
   "empty_column.follow_recommendations": "Näyttää siltä, että sinulle ei voi luoda ehdotuksia. Voit yrittää etsiä ihmisiä, jotka saatat tuntea tai tutkia trendaavia aihetunnisteita.",
@@ -230,8 +230,8 @@
   "empty_column.list": "Tässä luettelossa ei ole vielä mitään. Kun tämän luettelon jäsenet julkaisevat uusia viestejä, ne näkyvät täällä.",
   "empty_column.lists": "Sinulla ei ole vielä yhtään listaa. Kun luot sellaisen, näkyy se tässä.",
   "empty_column.mutes": "Et ole mykistänyt vielä yhtään käyttäjää.",
-  "empty_column.notifications": "Sinulla ei ole vielä ilmoituksia. Kun muut ihmiset ovat vuorovaikutuksessa kanssasi, näet sen täällä.",
-  "empty_column.public": "Täällä ei ole mitään! Kirjoita jotain julkisesti tai manuaalisesti seuraa muiden palvelimien käyttäjiä niin saat sisältöä",
+  "empty_column.notifications": "Sinulla ei ole vielä ilmoituksia. Kun keskustelet muille, näet sen täällä.",
+  "empty_column.public": "Täällä ei ole mitään! Kirjoita jotain julkisesti. Voit myös seurata muiden palvelimien käyttäjiä",
   "error.unexpected_crash.explanation": "Sivua ei voi näyttää oikein, johtuen bugista tai ongelmasta selaimen yhteensopivuudessa.",
   "error.unexpected_crash.explanation_addons": "Sivua ei voitu näyttää oikein. Tämä virhe johtuu todennäköisesti selaimen lisäosasta tai automaattisista käännöstyökaluista.",
   "error.unexpected_crash.next_steps": "Kokeile sivun päivitystä. Jos se ei auta, voi Mastodonin käyttö silti olla mahdollista eri selaimella tai natiivilla sovelluksella.",
@@ -242,7 +242,7 @@
   "explore.suggested_follows": "Sinulle",
   "explore.title": "Selaa",
   "explore.trending_links": "Uutiset",
-  "explore.trending_statuses": "Viestit",
+  "explore.trending_statuses": "Julkaisut",
   "explore.trending_tags": "Aihetunnisteet",
   "filter_modal.added.context_mismatch_explanation": "Tämä suodatinluokka ei koske asiayhteyttä, jossa olet käyttänyt tätä viestiä. Jos haluat, että viesti suodatetaan myös tässä yhteydessä, sinun on muokattava suodatinta.",
   "filter_modal.added.context_mismatch_title": "Asiayhteys ei täsmää!",
@@ -265,7 +265,7 @@
   "follow_recommendations.lead": "Seuraamiesi julkaisut näkyvät aikajärjestyksessä kotisyötteessä. Älä pelkää seurata vahingossa, voit lopettaa seuraamisen yhtä helposti!",
   "follow_request.authorize": "Valtuuta",
   "follow_request.reject": "Hylkää",
-  "follow_requests.unlocked_explanation": "Vaikkei tiliäsi ole lukittu, palvelun {domain} ylläpito on arvioinut, että voi olla halukas tarkistamaan nämä seurauspyynnöt erikseen.",
+  "follow_requests.unlocked_explanation": "Vaikkei tiliäsi ole lukittu, on palvelun {domain} ylläpito arvioinut, että saatat olla halukas tarkistamaan nämä seurauspyynnöt erikseen.",
   "followed_tags": "Seuratut aihetunnisteet",
   "footer.about": "Tietoja",
   "footer.directory": "Profiilihakemisto",
@@ -316,7 +316,7 @@
   "keyboard_shortcuts.description": "Kuvaus",
   "keyboard_shortcuts.direct": "avataksesi yksityisviestisarakkeen",
   "keyboard_shortcuts.down": "Siirry listassa alaspäin",
-  "keyboard_shortcuts.enter": "Avaa viesti",
+  "keyboard_shortcuts.enter": "Avaa julkaisu",
   "keyboard_shortcuts.favourite": "Lisää suosikkeihin",
   "keyboard_shortcuts.favourites": "Avaa lista suosikeista",
   "keyboard_shortcuts.federated": "Avaa yleinen aikajana",
@@ -328,7 +328,7 @@
   "keyboard_shortcuts.mention": "Mainitse julkaisija",
   "keyboard_shortcuts.muted": "Avaa lista mykistetyistä käyttäjistä",
   "keyboard_shortcuts.my_profile": "Avaa profiilisi",
-  "keyboard_shortcuts.notifications": "Avaa ilmoitukset-sarake",
+  "keyboard_shortcuts.notifications": "Avaa ilmoitukset-valikko",
   "keyboard_shortcuts.open_media": "Avaa media",
   "keyboard_shortcuts.pinned": "Avaa lista kiinnitetyistä viesteistä",
   "keyboard_shortcuts.profile": "Avaa kirjoittajan profiili",
@@ -336,10 +336,10 @@
   "keyboard_shortcuts.requests": "Avaa lista seurauspyynnöistä",
   "keyboard_shortcuts.search": "siirry hakukenttään",
   "keyboard_shortcuts.spoilers": "Näytä/piilota sisältövaroituskenttä",
-  "keyboard_shortcuts.start": "avaa \"Aloitus\"-sarake",
+  "keyboard_shortcuts.start": "avaa \"Aloitus\"",
   "keyboard_shortcuts.toggle_hidden": "näytä/piilota sisältövaroituksella merkitty teksti",
   "keyboard_shortcuts.toggle_sensitivity": "näytä/piilota media",
-  "keyboard_shortcuts.toot": "Aloita uusi viesti",
+  "keyboard_shortcuts.toot": "Luo uusi julkaisu",
   "keyboard_shortcuts.unfocus": "Poistu teksti-/hakukentästä",
   "keyboard_shortcuts.up": "Siirry listassa ylöspäin",
   "lightbox.close": "Sulje",
@@ -375,7 +375,7 @@
   "navigation_bar.blocks": "Estetyt käyttäjät",
   "navigation_bar.bookmarks": "Kirjanmerkit",
   "navigation_bar.community_timeline": "Paikallinen aikajana",
-  "navigation_bar.compose": "Luo uusi viesti",
+  "navigation_bar.compose": "Julkaise",
   "navigation_bar.direct": "Yksityisviestit",
   "navigation_bar.discover": "Löydä uutta",
   "navigation_bar.domain_blocks": "Estetyt palvelut",
@@ -395,10 +395,10 @@
   "navigation_bar.public_timeline": "Yleinen aikajana",
   "navigation_bar.search": "Haku",
   "navigation_bar.security": "Turvallisuus",
-  "not_signed_in_indicator.not_signed_in": "Sinun täytyy kirjautua sisään päästäksesi käsiksi tähän resurssiin.",
-  "notification.admin.report": "{name} ilmoitti {target}",
+  "not_signed_in_indicator.not_signed_in": "Sinun tulee kirjautua sisään nähdäksesi tämän.",
+  "notification.admin.report": "{name} teki ilmoituksen käytäjästä {target}",
   "notification.admin.sign_up": "{name} rekisteröityi",
-  "notification.favourite": "{name} tykkäsi viestistäsi",
+  "notification.favourite": "{name} tykkäsi julkaisustasi",
   "notification.follow": "{name} seurasi sinua",
   "notification.follow_request": "{name} haluaa seurata sinua",
   "notification.mention": "{name} mainitsi sinut",
@@ -409,7 +409,7 @@
   "notification.update": "{name} muokkasi viestiä",
   "notifications.clear": "Tyhjennä ilmoitukset",
   "notifications.clear_confirmation": "Haluatko varmasti poistaa kaikki ilmoitukset pysyvästi?",
-  "notifications.column_settings.admin.report": "Uudet raportit:",
+  "notifications.column_settings.admin.report": "Uudet ilmoitukset:",
   "notifications.column_settings.admin.sign_up": "Uudet kirjautumiset:",
   "notifications.column_settings.alert": "Työpöytäilmoitukset",
   "notifications.column_settings.favourite": "Tykkäykset:",
@@ -424,7 +424,7 @@
   "notifications.column_settings.reblog": "Tehostukset:",
   "notifications.column_settings.show": "Näytä sarakkeessa",
   "notifications.column_settings.sound": "Äänimerkki",
-  "notifications.column_settings.status": "Uudet viestit:",
+  "notifications.column_settings.status": "Uudet julkaisut:",
   "notifications.column_settings.unread_notifications.category": "Lukemattomat ilmoitukset",
   "notifications.column_settings.unread_notifications.highlight": "Korosta lukemattomat ilmoitukset",
   "notifications.column_settings.update": "Muokkaukset:",
@@ -474,32 +474,32 @@
   "relative_time.full.just_now": "juuri nyt",
   "relative_time.full.minutes": "{number, plural, one {# minuutti} other {# minuuttia}} sitten",
   "relative_time.full.seconds": "{number, plural, one {# sekunti} other {# sekuntia}} sitten",
-  "relative_time.hours": "{number} tuntia",
+  "relative_time.hours": "{number} t",
   "relative_time.just_now": "nyt",
   "relative_time.minutes": "{number} min",
-  "relative_time.seconds": "{number} sek",
+  "relative_time.seconds": "{number} s",
   "relative_time.today": "tänään",
   "reply_indicator.cancel": "Peruuta",
   "report.block": "Estä",
-  "report.block_explanation": "Et näe heidän viestejään, eivätkä he voi nähdä viestejäsi tai seurata sinua. He näkevät, että heidät on estetty.",
-  "report.categories.other": "Muu",
+  "report.block_explanation": "Et näe hänen viestejään, eikä hän voi nähdä viestejäsi tai seurata sinua. Hän näkevät, että olet estänyt hänet.",
+  "report.categories.other": "muu",
   "report.categories.spam": "Roskaposti",
   "report.categories.violation": "Sisältö rikkoo yhtä tai useampaa palvelimen sääntöä",
-  "report.category.subtitle": "Valitse paras vastaavuus",
-  "report.category.title": "Kerro meille miksi tämä {type} pitää raportoida",
+  "report.category.subtitle": "Valitse se, mikä sopii parhaiten",
+  "report.category.title": "Kerro meille, miksi ilmiannat tämän: {type} ",
   "report.category.title_account": "profiili",
-  "report.category.title_status": "viesti",
+  "report.category.title_status": "julkaisu",
   "report.close": "Valmis",
-  "report.comment.title": "Pitäisikö meidän tietää jotain muuta?",
+  "report.comment.title": "Olisiko jotain muuta, mitä meidän pitäisi tietää?",
   "report.forward": "Välitä kohteeseen {target}",
   "report.forward_hint": "Tämä tili on toisella palvelimella. Haluatko lähettää nimettömän raportin myös sinne?",
   "report.mute": "Mykistä",
-  "report.mute_explanation": "Et näe heidän viestejään. He voivat silti seurata sinua ja nähdä viestisi eivätkä tiedä, että heidät on mykistetty.",
+  "report.mute_explanation": "Et näe hänen viestejään. Hän voi silti seurata sinua ja nähdä viestisi. Hän ei tiedä, että on mykistetty.",
   "report.next": "Seuraava",
   "report.placeholder": "Lisäkommentit",
   "report.reasons.dislike": "En pidä siitä",
   "report.reasons.dislike_description": "Et halua nähdä sitä",
-  "report.reasons.other": "Se on jotain muuta",
+  "report.reasons.other": "Jotain muuta",
   "report.reasons.other_description": "Ongelma ei sovi muihin kategorioihin",
   "report.reasons.spam": "Se on roskapostia",
   "report.reasons.spam_description": "Haitalliset linkit, väärennetyt sitoutumiset tai toistuvat vastaukset",
@@ -507,27 +507,27 @@
   "report.reasons.violation_description": "Tiedät, että se rikkoo tiettyjä sääntöjä",
   "report.rules.subtitle": "Valitse kaikki jotka sopivat",
   "report.rules.title": "Mitä sääntöjä rikotaan?",
-  "report.statuses.subtitle": "Valitse kaikki jotka sopivat",
+  "report.statuses.subtitle": "Valitse kaikki sopivat",
   "report.statuses.title": "Onko olemassa yhtään viestiä, jotka tukevat tätä raporttia?",
   "report.submit": "Lähetä",
   "report.target": "Raportoidaan {target}",
   "report.thanks.take_action": "Tässä on vaihtoehtosi hallita näkemääsi Mastodonissa:",
   "report.thanks.take_action_actionable": "Sillä välin kun tarkistamme tätä, voit ryhtyä toimenpiteisiin käyttäjää @{name} vastaan:",
   "report.thanks.title": "Etkö halua nähdä tätä?",
-  "report.thanks.title_actionable": "Kiitos raportista, tutkimme asiaa.",
+  "report.thanks.title_actionable": "Kiitos ilmoituksesta, tarkistamme asian.",
   "report.unfollow": "Lopeta käyttäjän @{name} seuraaminen",
   "report.unfollow_explanation": "Seuraat tätä tiliä. Jotta et enää näe tilin viestejä, lopeta tilin seuraaminen.",
   "report_notification.attached_statuses": "{count, plural, one {{count} viesti} other {{count} viestiä}} liitteenä",
   "report_notification.categories.other": "Muu",
   "report_notification.categories.spam": "Roskaposti",
   "report_notification.categories.violation": "Sääntöjen rikkominen",
-  "report_notification.open": "Avaa raportti",
+  "report_notification.open": "Avaa ilmoitus",
   "search.placeholder": "Hae",
   "search.search_or_paste": "Etsi tai kirjoita URL-osoite",
   "search_popout.search_format": "Tarkennettu haku",
   "search_popout.tips.full_text": "Tekstihaku listaa tilapäivitykset, jotka olet kirjoittanut, lisännyt suosikkeihisi, tehostanut tai joissa sinut mainitaan, sekä tekstin sisältävät käyttäjänimet, nimimerkit ja aihetunnisteet.",
   "search_popout.tips.hashtag": "aihetunnisteet",
-  "search_popout.tips.status": "viesti",
+  "search_popout.tips.status": "julkaisu",
   "search_popout.tips.text": "Tekstihaku listaa hakua vastaavat nimimerkit, käyttäjänimet ja aihetunnisteet",
   "search_popout.tips.user": "käyttäjä",
   "search_results.accounts": "Ihmiset",
@@ -558,6 +558,7 @@
   "status.delete": "Poista",
   "status.detailed_status": "Yksityiskohtainen keskustelunäkymä",
   "status.direct": "Yksityisviesti käyttäjälle @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Muokkaa",
   "status.edited": "Muokattu {date}",
   "status.edited_x_times": "Muokattu {count, plural, one {{count} kerran} other {{count} kertaa}}",
@@ -576,7 +577,7 @@
   "status.mute_conversation": "Mykistä keskustelu",
   "status.open": "Laajenna julkaisu",
   "status.pin": "Kiinnitä profiiliin",
-  "status.pinned": "Kiinnitetty viesti",
+  "status.pinned": "Kiinnitetty julkaisu",
   "status.read_more": "Näytä enemmän",
   "status.reblog": "Tehosta",
   "status.reblog_private": "Tehosta alkuperäiselle yleisölle",
@@ -597,7 +598,7 @@
   "status.show_more_all": "Näytä lisää kaikista",
   "status.show_original": "Näytä alkuperäinen",
   "status.translate": "Käännä",
-  "status.translated_from_with": "Käännetty kielestä {lang} käyttäen palvelua {provider}",
+  "status.translated_from_with": "Käännetty kielestä {lang} käyttäen {provider}",
   "status.uncached_media_warning": "Ei saatavilla",
   "status.unmute_conversation": "Poista keskustelun mykistys",
   "status.unpin": "Irrota profiilista",
diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json
index db2247c4b..778b0e028 100644
--- a/app/javascript/mastodon/locales/fo.json
+++ b/app/javascript/mastodon/locales/fo.json
@@ -558,6 +558,7 @@
   "status.delete": "Strika",
   "status.detailed_status": "Útgreinað samrøðusýni",
   "status.direct": "Beinleiðis boð @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Rætta",
   "status.edited": "Rættað {date}",
   "status.edited_x_times": "Rættað {count, plural, one {{count} ferð} other {{count} ferð}}",
diff --git a/app/javascript/mastodon/locales/fr-QC.json b/app/javascript/mastodon/locales/fr-QC.json
index fe7055d57..e14b816bb 100644
--- a/app/javascript/mastodon/locales/fr-QC.json
+++ b/app/javascript/mastodon/locales/fr-QC.json
@@ -558,6 +558,7 @@
   "status.delete": "Supprimer",
   "status.detailed_status": "Vue détaillée de la conversation",
   "status.direct": "Envoyer un message direct à @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Modifier",
   "status.edited": "Modifiée le {date}",
   "status.edited_x_times": "Modifiée {count, plural, one {{count} fois} other {{count} fois}}",
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index 2c38ad496..8f2a886fb 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -558,6 +558,7 @@
   "status.delete": "Supprimer",
   "status.detailed_status": "Vue détaillée de la conversation",
   "status.direct": "Envoyer un message direct à @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Éditer",
   "status.edited": "Édité le {date}",
   "status.edited_x_times": "Edité {count, plural, one {{count} fois} other {{count} fois}}",
diff --git a/app/javascript/mastodon/locales/fy.json b/app/javascript/mastodon/locales/fy.json
index 4ac165471..f806494b7 100644
--- a/app/javascript/mastodon/locales/fy.json
+++ b/app/javascript/mastodon/locales/fy.json
@@ -558,6 +558,7 @@
   "status.delete": "Fuortsmite",
   "status.detailed_status": "Detaillearre petearoersjoch",
   "status.direct": "@{name} in direkt berjocht stjoere",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Bewurkje",
   "status.edited": "Bewurke op {date}",
   "status.edited_x_times": "{count, plural, one {{count} kear} other {{count} kearen}} bewurke",
diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json
index 12769ba13..8fc21a5a2 100644
--- a/app/javascript/mastodon/locales/ga.json
+++ b/app/javascript/mastodon/locales/ga.json
@@ -558,6 +558,7 @@
   "status.delete": "Scrios",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Seol teachtaireacht dhíreach chuig @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Cuir in eagar",
   "status.edited": "Curtha in eagar in {date}",
   "status.edited_x_times": "Curtha in eagar {count, plural, one {{count} uair amháin} two {{count} uair} few {{count} uair} many {{count} uair} other {{count} uair}}",
diff --git a/app/javascript/mastodon/locales/gd.json b/app/javascript/mastodon/locales/gd.json
index f5eb5c140..e4ae57952 100644
--- a/app/javascript/mastodon/locales/gd.json
+++ b/app/javascript/mastodon/locales/gd.json
@@ -558,6 +558,7 @@
   "status.delete": "Sguab às",
   "status.detailed_status": "Mion-shealladh a’ chòmhraidh",
   "status.direct": "Cuir teachdaireachd dhìreach gu @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Deasaich",
   "status.edited": "Air a dheasachadh {date}",
   "status.edited_x_times": "Chaidh a dheasachadh {count, plural, one {{counter} turas} two {{counter} thuras} few {{counter} tursan} other {{counter} turas}}",
diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json
index dc5bd31d6..b1ce477a6 100644
--- a/app/javascript/mastodon/locales/gl.json
+++ b/app/javascript/mastodon/locales/gl.json
@@ -558,6 +558,7 @@
   "status.delete": "Eliminar",
   "status.detailed_status": "Vista detallada da conversa",
   "status.direct": "Mensaxe directa a @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Editar",
   "status.edited": "Editado {date}",
   "status.edited_x_times": "Editado {count, plural, one {{count} vez} other {{count} veces}}",
diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json
index f5e6230b8..a827e4378 100644
--- a/app/javascript/mastodon/locales/he.json
+++ b/app/javascript/mastodon/locales/he.json
@@ -153,7 +153,7 @@
   "confirmations.block.confirm": "לחסום",
   "confirmations.block.message": "האם את/ה בטוח/ה שברצונך למחוק את \"{name}\"?",
   "confirmations.cancel_follow_request.confirm": "ויתור על בקשה",
-  "confirmations.cancel_follow_request.message": "האם באמת לוותר על בקשת המעקב אחרי {name}?",
+  "confirmations.cancel_follow_request.message": "לבטל את בקשת המעקב אחרי {name}?",
   "confirmations.delete.confirm": "למחוק",
   "confirmations.delete.message": "בטוח/ה שאת/ה רוצה למחוק את ההודעה?",
   "confirmations.delete_list.confirm": "למחוק",
@@ -163,7 +163,7 @@
   "confirmations.domain_block.confirm": "חסמו לגמרי את שם המתחם (דומיין)",
   "confirmations.domain_block.message": "בטוחה שברצונך באמת לחסום את קהילת {domain}? ברב המקרים השתקה וחסימה של מספר משתמשים עשוייה להספיק. לא תראי תוכל מכלל שם המתחם בפידים הציבוריים או בהתראות שלך. העוקבים שלך מהקהילה הזאת יוסרו",
   "confirmations.edit.confirm": "עריכה",
-  "confirmations.edit.message": "עריכת תגובה קודמת תדרוס את ההודעה שכבר התחלת לכתוב. האם את.ה בטוח.ה שברצונך להמשיך?",
+  "confirmations.edit.message": "עריכה תדרוס את ההודעה שכבר התחלת לכתוב. האם להמשיך?",
   "confirmations.logout.confirm": "התנתקות",
   "confirmations.logout.message": "האם אתם בטוחים שאתם רוצים להתנתק?",
   "confirmations.mute.confirm": "להשתיק",
@@ -172,7 +172,7 @@
   "confirmations.redraft.confirm": "מחיקה ועריכה מחדש",
   "confirmations.redraft.message": "בטוחה שאת רוצה למחוק ולהתחיל טיוטה חדשה? חיבובים והדהודים יאבדו, ותגובות להודעה המקורית ישארו יתומות.",
   "confirmations.reply.confirm": "תגובה",
-  "confirmations.reply.message": "תגובה עכשיו תדרוס את ההודעה שכבר התחלתים לכתוב. האם אתם בטוחים שברצונכם להמשיך?",
+  "confirmations.reply.message": "תגובה עכשיו תמחק את ההודעה שכבר התחלת לכתוב. להמשיך?",
   "confirmations.unfollow.confirm": "הפסקת מעקב",
   "confirmations.unfollow.message": "להפסיק מעקב אחרי {name}?",
   "conversation.delete": "מחיקת שיחה",
@@ -558,6 +558,7 @@
   "status.delete": "מחיקה",
   "status.detailed_status": "תצוגת שיחה מפורטת",
   "status.direct": "הודעה ישירה ל@{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "עריכה",
   "status.edited": "נערך ב{date}",
   "status.edited_x_times": "נערך {count, plural, one {פעם {count}} other {{count} פעמים}}",
diff --git a/app/javascript/mastodon/locales/hi.json b/app/javascript/mastodon/locales/hi.json
index 8b0569860..8e311c945 100644
--- a/app/javascript/mastodon/locales/hi.json
+++ b/app/javascript/mastodon/locales/hi.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json
index 2ee74d460..196d43e60 100644
--- a/app/javascript/mastodon/locales/hr.json
+++ b/app/javascript/mastodon/locales/hr.json
@@ -558,6 +558,7 @@
   "status.delete": "Obriši",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Uredi",
   "status.edited": "Uređeno {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
index 4447aa2d1..c69b58e43 100644
--- a/app/javascript/mastodon/locales/hu.json
+++ b/app/javascript/mastodon/locales/hu.json
@@ -34,7 +34,7 @@
   "account.followers.empty": "Ezt a felhasználót még senki sem követi.",
   "account.followers_counter": "{count, plural, one {{counter} Követő} other {{counter} Követő}}",
   "account.following": "Követve",
-  "account.following_counter": "{count, plural, one {{counter} Követett} other {{counter} Követett}}",
+  "account.following_counter": "{count, plural, one {{counter} követett} other {{counter} követett}}",
   "account.follows.empty": "Ez a felhasználó még senkit sem követ.",
   "account.follows_you": "Követ téged",
   "account.go_to_profile": "Ugrás a profilhoz",
@@ -53,8 +53,8 @@
   "account.posts": "Bejegyzések",
   "account.posts_with_replies": "Bejegyzések és válaszok",
   "account.report": "@{name} jelentése",
-  "account.requested": "Jóváhagysára vár. Kattints a követési kérés visszavonásához",
-  "account.requested_follow": "{name} kérte, hogy követhessen téged",
+  "account.requested": "Jóváhagysára vár. Kattintás a követési kérés törléséhez",
+  "account.requested_follow": "{name} kérte, hogy követhessen",
   "account.share": "@{name} profiljának megosztása",
   "account.show_reblogs": "@{name} megtolásainak mutatása",
   "account.statuses_counter": "{count, plural, one {{counter} Bejegyzés} other {{counter} Bejegyzés}}",
@@ -66,7 +66,7 @@
   "account.unmute": "@{name} némításának feloldása",
   "account.unmute_notifications": "@{name} némított értesítéseinek feloldása",
   "account.unmute_short": "Némitás feloldása",
-  "account_note.placeholder": "Kattints jegyzet hozzáadásához",
+  "account_note.placeholder": "Kattintás jegyzet hozzáadásához",
   "admin.dashboard.daily_retention": "Napi regisztráció utáni felhasználómegtartási arány",
   "admin.dashboard.monthly_retention": "Havi regisztráció utáni felhasználómegtartási arány",
   "admin.dashboard.retention.average": "Átlag",
@@ -96,7 +96,7 @@
   "closed_registrations.other_server_instructions": "Mivel a Mastdon decentralizált, létrehozhatsz egy fiókot egy másik kiszolgálón és mégis kapcsolódhatsz ehhez.",
   "closed_registrations_modal.description": "Fiók létrehozása a {domain} kiszolgálón jelenleg nem lehetséges, de jó, ha tudod, hogy nem szükséges fiókkal rendelkezni pont a {domain} kiszolgálón, hogy használhasd a Mastodont.",
   "closed_registrations_modal.find_another_server": "Másik kiszolgáló keresése",
-  "closed_registrations_modal.preamble": "A Mastodon decentralizált, így teljesen mindegy, hol hozod létre a fiókodat, követhetsz és kapcsolódhatsz bárkivel ezen a kiszolgálón is. Saját magad is üzemeltethetsz kiszolgálót!",
+  "closed_registrations_modal.preamble": "A Mastodon nem központosított, így teljesen mindegy, hol történik a fiók létrehozása, követhető bárki és kapcsolatba lehet lépni bárkivel ezen a kiszolgálón is. Saját magunk is üzemeltethetünk kiszolgálót!",
   "closed_registrations_modal.title": "Regisztráció a Mastodonra",
   "column.about": "Névjegy",
   "column.blocks": "Letiltott felhasználók",
@@ -131,7 +131,7 @@
   "compose_form.hashtag_warning": "Ez a bejegyzésed nem fog megjelenni semmilyen hashtag alatt, mivel nem nyilvános. Csak a nyilvános bejegyzések kereshetők hashtaggel.",
   "compose_form.lock_disclaimer": "A fiókod nincs {locked}. Bárki követni tud, hogy megtekintse a kizárólag követőknek szánt bejegyzéseket.",
   "compose_form.lock_disclaimer.lock": "lezárva",
-  "compose_form.placeholder": "Mi jár a fejedben?",
+  "compose_form.placeholder": "Mire gondolunk éppen?",
   "compose_form.poll.add_option": "Lehetőség hozzáadása",
   "compose_form.poll.duration": "Szavazás időtartama",
   "compose_form.poll.option_placeholder": "{number}. lehetőség",
@@ -145,25 +145,25 @@
   "compose_form.sensitive.hide": "{count, plural, one {Média kényesnek jelölése} other {Média kényesnek jelölése}}",
   "compose_form.sensitive.marked": "{count, plural, one {A médiát kényesnek jelölték} other {A médiát kényesnek jelölték}}",
   "compose_form.sensitive.unmarked": "{count, plural, one {A médiát nem jelölték kényesnek} other {A médiát nem jelölték kényesnek}}",
-  "compose_form.spoiler.marked": "Tartalmi figyelmeztetés törlése",
+  "compose_form.spoiler.marked": "Tartalmi figyelmeztetés eltávolítása",
   "compose_form.spoiler.unmarked": "Tartalmi figyelmeztetés hozzáadása",
-  "compose_form.spoiler_placeholder": "Írd ide a figyelmeztetést",
-  "confirmation_modal.cancel": "Mégse",
+  "compose_form.spoiler_placeholder": "A figyelmeztetés beírása ide",
+  "confirmation_modal.cancel": "Mégsem",
   "confirmations.block.block_and_report": "Letiltás és jelentés",
   "confirmations.block.confirm": "Letiltás",
   "confirmations.block.message": "Biztos, hogy letiltod: {name}?",
   "confirmations.cancel_follow_request.confirm": "Kérés visszavonása",
-  "confirmations.cancel_follow_request.message": "Biztos, hogy visszavonod a(z) {name} felhasználóra vonatkozó követési kérésedet?",
+  "confirmations.cancel_follow_request.message": "Biztosan visszavonásra kerüljön {name} felhasználóra vonatkozó követési kérés?",
   "confirmations.delete.confirm": "Törlés",
   "confirmations.delete.message": "Biztos, hogy törölni szeretnéd ezt a bejegyzést?",
   "confirmations.delete_list.confirm": "Törlés",
   "confirmations.delete_list.message": "Biztos, hogy véglegesen törölni szeretnéd ezt a listát?",
   "confirmations.discard_edit_media.confirm": "Elvetés",
-  "confirmations.discard_edit_media.message": "Elmentetlen változtatásaid vannak a média leírásában vagy előnézetében. Eldobjuk őket?",
-  "confirmations.domain_block.confirm": "Teljes domain elrejtése",
+  "confirmations.discard_edit_media.message": "Elmentetlen változtatások vannak a média leírásában vagy előnézetében. Elvetésre kerüljenek?",
+  "confirmations.domain_block.confirm": "Teljes tartomány tiltása",
   "confirmations.domain_block.message": "Biztos, hogy le szeretnéd tiltani a teljes {domain} domaint? A legtöbb esetben néhány célzott tiltás vagy némítás elegendő, és kívánatosabb megoldás. Semmilyen tartalmat nem fogsz látni ebből a domainből se az idővonalakon, se az értesítésekben. Az ebben a domainben lévő követőidet is eltávolítjuk.",
   "confirmations.edit.confirm": "Szerkesztés",
-  "confirmations.edit.message": "Ha most szerkeszted, ez felülírja a most szerkesztés alatt álló üzenetet. Mégis ezt szeretnéd?",
+  "confirmations.edit.message": "A szerkesztés felülírja a most összeállítás alatt álló üzenetet. Folytatás?",
   "confirmations.logout.confirm": "Kijelentkezés",
   "confirmations.logout.message": "Biztos, hogy kijelentkezel?",
   "confirmations.mute.confirm": "Némítás",
@@ -178,21 +178,21 @@
   "conversation.delete": "Beszélgetés törlése",
   "conversation.mark_as_read": "Megjelölés olvasottként",
   "conversation.open": "Beszélgetés megtekintése",
-  "conversation.with": "{names}-el/al",
+  "conversation.with": "{names} is",
   "copypaste.copied": "Másolva",
   "copypaste.copy": "Másolás",
   "directory.federated": "Az ismert fediverzumból",
-  "directory.local": "Csak innen: {domain}",
+  "directory.local": "Csak {domain} tartományból",
   "directory.new_arrivals": "Új csatlakozók",
   "directory.recently_active": "Nemrég aktív",
   "disabled_account_banner.account_settings": "Fiókbeállítások",
-  "disabled_account_banner.text": "A(z) {disabledAccount} fiókod jelenleg le van tiltva.",
-  "dismissable_banner.community_timeline": "Ezek a legfrissebb nyilvános bejegyzések, amelyeket a(z) {domain} kiszolgáló fiókjait használó emberek tették közzé.",
-  "dismissable_banner.dismiss": "Eltüntetés",
-  "dismissable_banner.explore_links": "Jelenleg ezekről a hírekről beszélgetnek az ezen és a decentralizált hálózat többi kiszolgálóján lévő emberek.",
-  "dismissable_banner.explore_statuses": "Jelenleg ezek a bejegyzések hódítanak teret ezen és a decentralizált hálózat egyéb kiszolgálóin.",
-  "dismissable_banner.explore_tags": "Jelenleg ezek a hashtagek hódítanak teret ezen és a decentralizált hálózat többi kiszolgálóján lévő emberek körében.",
-  "dismissable_banner.public_timeline": "Ezek a legfrissebb bejegyzések azoktól, akik a decentralizált hálózat más kiszolgálóin vannak, és ez a kiszolgáló tud róluk.",
+  "disabled_account_banner.text": "{disabledAccount} fiók jelenleg letilzásra került.",
+  "dismissable_banner.community_timeline": "Ezek a legfrissebb nyilvános bejegyzések, amelyeket {domain} tartományban levő kiszolgáló fiókjait használó emberek tettek közzé.",
+  "dismissable_banner.dismiss": "Elvetés",
+  "dismissable_banner.explore_links": "Jelenleg ezekről a hírekről beszélgetnek az ezen és a központosítás nélküli hálózat többi kiszolgálóján lévő emberek.",
+  "dismissable_banner.explore_statuses": "Jelenleg ezek a bejegyzések hódítanak teret ezen és a központosítás nélküli hálózat egyéb kiszolgálóin.",
+  "dismissable_banner.explore_tags": "Jelenleg ezek a #címke elemek hódítanak teret ezen és a központosítás nélküli hálózat többi kiszolgálóján lévő emberek körében.",
+  "dismissable_banner.public_timeline": "Ezek a legfrissebb bejegyzések azoktól, akik a központosítás nélküli hálózat más kiszolgálóin vannak és ez a kiszolgáló tud róluk.",
   "embed.instructions": "Ágyazd be ezt a bejegyzést a weboldaladba az alábbi kód kimásolásával.",
   "embed.preview": "Így fog kinézni:",
   "emoji_button.activity": "Tevékenység",
@@ -210,67 +210,67 @@
   "emoji_button.search_results": "Keresési találatok",
   "emoji_button.symbols": "Szimbólumok",
   "emoji_button.travel": "Utazás és Helyek",
-  "empty_column.account_suspended": "Fiók felfüggesztve",
+  "empty_column.account_suspended": "A fiók felfüggesztésre került",
   "empty_column.account_timeline": "Itt nincs bejegyzés!",
   "empty_column.account_unavailable": "A profil nem érhető el",
-  "empty_column.blocks": "Még senkit sem tiltottál le.",
+  "empty_column.blocks": "Még senki sem került letiltásra.",
   "empty_column.bookmarked_statuses": "Még nincs egyetlen könyvjelzőzött bejegyzésed sem. Ha könyvjelzőzöl egyet, itt fog megjelenni.",
   "empty_column.community": "A helyi idővonal üres. Tégy közzé valamit nyilvánosan, hogy elindítsd az eseményeket!",
-  "empty_column.direct": "Még nincs egy közvetlen üzeneted sem. Ha küldesz vagy kapsz egyet, itt fog megjelenni.",
-  "empty_column.domain_blocks": "Még nem rejtettél el egyetlen domaint sem.",
+  "empty_column.direct": "Még nincs egy közvetlen üzenet sem. Küldéskor vagy fogadáskor itt fog megjelenni.",
+  "empty_column.domain_blocks": "Még nem lett letiltva egyetlen tartomány sem.",
   "empty_column.explore_statuses": "Jelenleg semmi sem felkapott. Nézz vissza később!",
   "empty_column.favourited_statuses": "Még nincs egyetlen kedvenc bejegyzésed sem. Ha kedvencnek jelölsz egyet, itt fog megjelenni.",
   "empty_column.favourites": "Még senki sem jelölte ezt a bejegyzést kedvencnek. Ha valaki mégis megteszi, itt fogjuk mutatni.",
   "empty_column.follow_recommendations": "Úgy tűnik, neked nem tudunk javaslatokat adni. Próbáld a keresést használni olyanok megtalálására, akiket ismerhetsz, vagy fedezd fel a felkapott hastageket.",
-  "empty_column.follow_requests": "Még nincs egy követési kérésed sem. Ha kapsz egyet, itt fogjuk feltüntetni.",
-  "empty_column.followed_tags": "Még egy hashtaget sem követtél be. Itt fognak megjelenni, ahogy bekövetsz egyet.",
-  "empty_column.hashtag": "Jelenleg nem található semmi ezzel a hashtaggel.",
-  "empty_column.home": "A saját idővonalad üres! Látogasd meg a {public} oldalt vagy használd a keresőt, hogy megismerj másokat.",
+  "empty_column.follow_requests": "Még nincs egy követési kérés sem. Fogadáskor itt jelenik meg.",
+  "empty_column.followed_tags": "Még egy #címke sincs követve. Ezek ekkor itt jelennek meg.",
+  "empty_column.hashtag": "Jelenleg nem található semmi ezzel a #címkével.",
+  "empty_column.home": "A saját idővonal üres! További emberek követése a kitöltéshez. {suggestions}",
   "empty_column.home.suggestions": "Nézzünk pár javaslatot",
   "empty_column.list": "A lista jelenleg üres. Ha a listatagok bejegyzést tesznek közzé, itt fog megjelenni.",
-  "empty_column.lists": "Még nem hoztál létre listát. Ha csinálsz egyet, itt látszik majd.",
-  "empty_column.mutes": "Még egy felhasználót sem némítottál le.",
-  "empty_column.notifications": "Jelenleg nincsenek értesítéseid. Lépj kapcsolatba másokkal, hogy elindítsd a beszélgetést.",
-  "empty_column.public": "Jelenleg itt nincs semmi! Írj valamit nyilvánosan vagy kövess más kiszolgálón levő felhasználókat, hogy megtöltsd.",
+  "empty_column.lists": "Még nincs egyetlen lista sem. A létrehozáskor itt jelenik meg.",
+  "empty_column.mutes": "Még nincs egyetlen némított felhasználót sem.",
+  "empty_column.notifications": "Jelenleg nincsenek értesítések. Más emberekkel kapcsolatba lépés után ez itt lesz látható.",
+  "empty_column.public": "Jelenleg itt nincs semmi! Írjunk valamit nyilvánosan vagy kövessünk más kiszolgálón levő felhasználókat a megjelenéshez.",
   "error.unexpected_crash.explanation": "Egy hiba vagy böngésző inkompatibilitás miatt ez az oldal nem jeleníthető meg rendesen.",
-  "error.unexpected_crash.explanation_addons": "Ezt az oldalt nem lehet helyesen megjeleníteni. Ezt a hibát valószínűleg egy böngésző beépülő vagy egy automatikus fordító okozza.",
-  "error.unexpected_crash.next_steps": "Próbáld frissíteni az oldalt. Ha ez nem segít, egy másik böngészőn vagy appon keresztül még mindig használhatod a Mastodont.",
-  "error.unexpected_crash.next_steps_addons": "Próbáld letiltani őket és frissíteni az oldalt. Ha ez nem segít, egy másik böngészőn vagy appon keresztül még mindig használhatod a Mastodont.",
+  "error.unexpected_crash.explanation_addons": "Ezt az oldalt nem lehet helyesen megjeleníteni. Ezt a hibát valószínűleg egy böngésző kiegészítő vagy egy automatikus fordító okozza.",
+  "error.unexpected_crash.next_steps": "Próbáljuk meg frissíteni az oldalt. Ha ez nem segít, egy másik böngészőn vagy appon keresztül még mindig használható a Mastodon.",
+  "error.unexpected_crash.next_steps_addons": "Próbáljuk meg letiltani őket és frissíteni az oldalt. Ha ez nem segít, egy másik böngészőn vagy alkalmazáson keresztül még mindig használható a Mastodon.",
   "errors.unexpected_crash.copy_stacktrace": "Veremkiíratás vágólapra másolása",
   "errors.unexpected_crash.report_issue": "Probléma jelentése",
   "explore.search_results": "Keresési találatok",
-  "explore.suggested_follows": "Neked",
+  "explore.suggested_follows": "Nekem",
   "explore.title": "Felfedezés",
   "explore.trending_links": "Hírek",
   "explore.trending_statuses": "Bejegyzések",
-  "explore.trending_tags": "Hashtagek",
-  "filter_modal.added.context_mismatch_explanation": "Ez a szűrőkategória nem érvényes abban a környezetben, amelyből elérted ezt a bejegyzést. Ha ebben a környezetben is szűrni szeretnéd a bejegyzést, akkor szerkesztened kell a szűrőt.",
+  "explore.trending_tags": "#Címkék",
+  "filter_modal.added.context_mismatch_explanation": "Ez a szűrőkategória nem érvényes abban a környezetben, amelyből ez a bejegyzés elérésre kerül. Ha ebben a környezetben is szűrni szeretnénk a bejegyzést, akkor szerkeszteni kell a szűrőt.",
   "filter_modal.added.context_mismatch_title": "Környezeti eltérés.",
-  "filter_modal.added.expired_explanation": "Ez a szűrőkategória elévült, a használatához módosítanod kell az elévülési dátumot.",
-  "filter_modal.added.expired_title": "Elévült szűrő.",
-  "filter_modal.added.review_and_configure": "A szűrőkategória felülvizsgálatához és további beállításához ugorjon a {settings_link} oldalra.",
+  "filter_modal.added.expired_explanation": "Ez a szűrőkategória elévült, a használatához módosítani kell a lejárati dátumot.",
+  "filter_modal.added.expired_title": "A szűrő lejárt!",
+  "filter_modal.added.review_and_configure": "A szűrőkategória felülvizsgálatához és további beállításához ugorás {settings_link} oldalra.",
   "filter_modal.added.review_and_configure_title": "Szűrőbeállítások",
   "filter_modal.added.settings_link": "beállítások oldal",
   "filter_modal.added.short_explanation": "A következő bejegyzés hozzá lett adva a következő szűrőkategóriához: {title}.",
-  "filter_modal.added.title": "Szűrő hozzáadva.",
+  "filter_modal.added.title": "A szűrő hozzáadásra került.",
   "filter_modal.select_filter.context_mismatch": "nem érvényes erre a környezetre",
-  "filter_modal.select_filter.expired": "elévült",
+  "filter_modal.select_filter.expired": "lejárt",
   "filter_modal.select_filter.prompt_new": "Új kategória: {name}",
   "filter_modal.select_filter.search": "Keresés vagy létrehozás",
-  "filter_modal.select_filter.subtitle": "Válassz egy meglévő kategóriát, vagy hozz létre egy újat",
+  "filter_modal.select_filter.subtitle": "Létező kategória használata vagy új létrehozása",
   "filter_modal.select_filter.title": "E bejegyzés szűrése",
   "filter_modal.title.status": "Egy bejegyzés szűrése",
   "follow_recommendations.done": "Kész",
   "follow_recommendations.heading": "Kövesd azokat, akiknek a bejegyzéseit látni szeretnéd! Itt van néhány javaslat.",
   "follow_recommendations.lead": "Az általad követettek bejegyzései a saját idővonaladon fognak megjelenni időrendi sorrendben. Ne félj attól, hogy hibázol! A követést bármikor, ugyanilyen könnyen visszavonhatod!",
-  "follow_request.authorize": "Engedélyezés",
+  "follow_request.authorize": "Hitelesítés",
   "follow_request.reject": "Elutasítás",
   "follow_requests.unlocked_explanation": "Bár a fiókod nincs zárolva, a(z) {domain} csapata úgy gondolta, hogy talán kézzel szeretnéd ellenőrizni a fiók követési kéréseit.",
-  "followed_tags": "Követett hashtagek",
+  "followed_tags": "Követett #címkék",
   "footer.about": "Névjegy",
-  "footer.directory": "Profilok",
-  "footer.get_app": "Töltsd le az appot",
-  "footer.invite": "Mások meghívása",
+  "footer.directory": "Profiltár",
+  "footer.get_app": "Alkalmazás beszerzése",
+  "footer.invite": "Emberek meghívása",
   "footer.keyboard_shortcuts": "Billentyűparancsok",
   "footer.privacy_policy": "Adatvédelmi szabályzat",
   "footer.source_code": "Forráskód megtekintése",
@@ -281,11 +281,11 @@
   "hashtag.column_header.tag_mode.any": "vagy {additional}",
   "hashtag.column_header.tag_mode.none": "{additional} nélkül",
   "hashtag.column_settings.select.no_options_message": "Nincs javaslat",
-  "hashtag.column_settings.select.placeholder": "Addj meg hashtageket…",
+  "hashtag.column_settings.select.placeholder": "#Címkék megadása…",
   "hashtag.column_settings.tag_mode.all": "Mindegyik",
   "hashtag.column_settings.tag_mode.any": "Bármelyik",
   "hashtag.column_settings.tag_mode.none": "Egyik sem",
-  "hashtag.column_settings.tag_toggle": "Új címkék felvétele ehhez az oszlophoz",
+  "hashtag.column_settings.tag_toggle": "További címkék felvétele ehhez az oszlophoz",
   "hashtag.follow": "Hashtag követése",
   "hashtag.unfollow": "Hashtag követésének megszüntetése",
   "home.column_settings.basic": "Alapvető",
@@ -293,14 +293,14 @@
   "home.column_settings.show_replies": "Válaszok megjelenítése",
   "home.hide_announcements": "Közlemények elrejtése",
   "home.show_announcements": "Közlemények megjelenítése",
-  "interaction_modal.description.favourite": "Egy Mastodon fiókkal kedvencnek jelölheted ezt a bejegyzést, tudatva a szerzővel, hogy értékeled és elteszed későbbre.",
-  "interaction_modal.description.follow": "Egy Mastodon fiókkal bekövetheted {name} fiókot, hogy lásd a bejegyzéseit a saját hírfolyamodban.",
-  "interaction_modal.description.reblog": "Egy Mastodon fiókkal megtolhatod ezt a bejegyzést, hogy megoszd a saját követőiddel.",
-  "interaction_modal.description.reply": "Egy Mastodon fiókkal válaszolhatsz erre a bejegyzésre.",
+  "interaction_modal.description.favourite": "Egy Mastodon fiókkal kedvencnek jelölhető ez a bejegyzés, tudatva a szerzővel, hogy értékeljük és eltesszük későbbre.",
+  "interaction_modal.description.follow": "Egy Mastodon fiókkal bekövethető {name} fiók a bejegyzései megjelenítéséhez a saját hírfolyamban.",
+  "interaction_modal.description.reblog": "Egy Mastodon fiókkal megtolható ez a bejegyzés a saját követőkkel megosztáshoz.",
+  "interaction_modal.description.reply": "Egy Mastodon fiókkal válaszolhatunk erre a bejegyzésre.",
   "interaction_modal.on_another_server": "Másik kiszolgálón",
   "interaction_modal.on_this_server": "Ezen a kiszolgálón",
-  "interaction_modal.other_server_instructions": "Másold és illeszd be ezt a webcímet a kedvenc Mastodon alkalmazásod vagy a Mastodon-kiszolgálód webes felületének keresőmezőjébe.",
-  "interaction_modal.preamble": "Mivel a Mastodon decentralizált, használhatod egy másik Mastodon kiszolgálón, vagy kompatibilis szolgáltatáson lévő fiókodat, ha ezen a kiszolgálón nincs fiókod.",
+  "interaction_modal.other_server_instructions": "Másoljuk és illesszük be ezt a webcímet a kedvenc Mastodon alkalmazásd vagy a Mastodon kiszolgáló webes felületének keresőmezőjébe.",
+  "interaction_modal.preamble": "Mivel a Mastodon nem központosított, használható egy másik Mastodon kiszolgálón vagy kompatibilis szolgáltatáson lévő fiók, ha ezen a kiszolgálón nincs saját fiók.",
   "interaction_modal.title.favourite": "{name} bejegyzésének megjelölése kedvencként",
   "interaction_modal.title.follow": "{name} követése",
   "interaction_modal.title.reblog": "{name} bejegyzésének megtolása",
@@ -308,47 +308,47 @@
   "intervals.full.days": "{number, plural, one {# nap} other {# nap}}",
   "intervals.full.hours": "{number, plural, one {# óra} other {# óra}}",
   "intervals.full.minutes": "{number, plural, one {# perc} other {# perc}}",
-  "keyboard_shortcuts.back": "visszafelé navigálás",
-  "keyboard_shortcuts.blocked": "letiltott felhasználók listájának megnyitása",
+  "keyboard_shortcuts.back": "Navigálás vissza",
+  "keyboard_shortcuts.blocked": "Letiltott felhasználók listájának megnyitása",
   "keyboard_shortcuts.boost": "Bejegyzés megtolása",
   "keyboard_shortcuts.column": "Fókuszálás egy oszlopra",
-  "keyboard_shortcuts.compose": "fókuszálás a szerkesztési szövegdobozra",
+  "keyboard_shortcuts.compose": "Szerkesztési terület fókuszálása",
   "keyboard_shortcuts.description": "Leírás",
   "keyboard_shortcuts.direct": "közvetlen üzenetek megnyitása",
-  "keyboard_shortcuts.down": "lefele navigálás a listában",
+  "keyboard_shortcuts.down": "Mozgás lefelé a listában",
   "keyboard_shortcuts.enter": "Bejegyzés megnyitása",
   "keyboard_shortcuts.favourite": "Bejegyzés kedvencnek jelölése",
-  "keyboard_shortcuts.favourites": "kedvenc lista megnyitása",
+  "keyboard_shortcuts.favourites": "Kedvencek lista megnyitása",
   "keyboard_shortcuts.federated": "föderációs idővonal megnyitása",
   "keyboard_shortcuts.heading": "Billentyűparancsok",
-  "keyboard_shortcuts.home": "saját idővonal megnyitása",
+  "keyboard_shortcuts.home": "Saját idővonal megnyitása",
   "keyboard_shortcuts.hotkey": "Gyorsbillentyű",
   "keyboard_shortcuts.legend": "jelmagyarázat megjelenítése",
   "keyboard_shortcuts.local": "helyi idővonal megnyitása",
-  "keyboard_shortcuts.mention": "szerző megemlítése",
+  "keyboard_shortcuts.mention": "Szerző megemlítése",
   "keyboard_shortcuts.muted": "némított felhasználók listájának megnyitása",
-  "keyboard_shortcuts.my_profile": "profilod megnyitása",
-  "keyboard_shortcuts.notifications": "értesítések megnyitása",
-  "keyboard_shortcuts.open_media": "média megnyitása",
+  "keyboard_shortcuts.my_profile": "Saját profil megnyitása",
+  "keyboard_shortcuts.notifications": "Értesítések oszlop megnyitása",
+  "keyboard_shortcuts.open_media": "Média megnyitása",
   "keyboard_shortcuts.pinned": "Kitűzött bejegyzések listájának megnyitása",
-  "keyboard_shortcuts.profile": "szerző profiljának megnyitása",
+  "keyboard_shortcuts.profile": "Szerző profil megnyitása",
   "keyboard_shortcuts.reply": "Válasz bejegyzésre",
-  "keyboard_shortcuts.requests": "követési kérések listájának megnyitása",
-  "keyboard_shortcuts.search": "fókuszálás a keresőre",
+  "keyboard_shortcuts.requests": "Követési kérések lista megnyitása",
+  "keyboard_shortcuts.search": "Keresősáv fókuszálása",
   "keyboard_shortcuts.spoilers": "Tartalmi figyelmeztetés mező megjelenítése/elrejtése",
-  "keyboard_shortcuts.start": "\"Első lépések\" megnyitása",
-  "keyboard_shortcuts.toggle_hidden": "Tartalmi figyelmeztetéssel ellátott szöveg megjelenítése/elrejtése",
+  "keyboard_shortcuts.start": "\"Első lépések\" oszlop megnyitása",
+  "keyboard_shortcuts.toggle_hidden": "Tartalmi figyelmeztetéssel mögötti szöveg megjelenítése/elrejtése",
   "keyboard_shortcuts.toggle_sensitivity": "Média megjelenítése/elrejtése",
   "keyboard_shortcuts.toot": "Új bejegyzés írása",
   "keyboard_shortcuts.unfocus": "Szerkesztés/keresés fókuszból való kivétele",
-  "keyboard_shortcuts.up": "felfelé mozdítás a listában",
+  "keyboard_shortcuts.up": "Mozgás felfelé a listában",
   "lightbox.close": "Bezárás",
-  "lightbox.compress": "Képnézet összecsukása",
-  "lightbox.expand": "Képnézet kinagyítása",
+  "lightbox.compress": "Képnéző doboz összezárása",
+  "lightbox.expand": "Képnéző doboz kinyitása",
   "lightbox.next": "Következő",
   "lightbox.previous": "Előző",
-  "limited_account_hint.action": "Mindenképpen mutassa a profilt",
-  "limited_account_hint.title": "Ezt a profilt a(z) {domain} moderátorai elrejtették.",
+  "limited_account_hint.action": "Profil megjelenítése mindenképpen",
+  "limited_account_hint.title": "Ezt a profilt {domain} moderátorai elrejtették.",
   "lists.account.add": "Hozzáadás a listához",
   "lists.account.remove": "Eltávolítás a listából",
   "lists.delete": "Lista törlése",
@@ -359,17 +359,17 @@
   "lists.replies_policy.followed": "Bármely követett felhasználó",
   "lists.replies_policy.list": "A lista tagjai",
   "lists.replies_policy.none": "Senki",
-  "lists.replies_policy.title": "Nekik mutassuk a válaszokat:",
+  "lists.replies_policy.title": "Válaszok megjelenítése:",
   "lists.search": "Keresés a követett személyek között",
-  "lists.subheading": "Listáid",
+  "lists.subheading": "Saját listák",
   "load_pending": "{count, plural, one {# új elem} other {# új elem}}",
   "loading_indicator.label": "Betöltés...",
   "media_gallery.toggle_visible": "{number, plural, one {Kép elrejtése} other {Képek elrejtése}}",
   "missing_indicator.label": "Nincs találat",
   "missing_indicator.sublabel": "Ez az erőforrás nem található",
-  "moved_to_account_banner.text": "A(z) {disabledAccount} fiókod jelenleg le van tiltva, mert átköltöztél ide: {movedToAccount}.",
+  "moved_to_account_banner.text": "{disabledAccount} fiók jelenleg le van tiltva, mert más {movedToAccount} fiókba került át.",
   "mute_modal.duration": "Időtartam",
-  "mute_modal.hide_notifications": "Rejtsük el a felhasználótól származó értesítéseket?",
+  "mute_modal.hide_notifications": "Értesítések elrejtése ettől a felhasználótól?",
   "mute_modal.indefinite": "Határozatlan",
   "navigation_bar.about": "Névjegy",
   "navigation_bar.blocks": "Letiltott felhasználók",
@@ -378,13 +378,13 @@
   "navigation_bar.compose": "Új bejegyzés írása",
   "navigation_bar.direct": "Közvetlen üzenetek",
   "navigation_bar.discover": "Felfedezés",
-  "navigation_bar.domain_blocks": "Rejtett domainek",
+  "navigation_bar.domain_blocks": "Letiltott tartományok",
   "navigation_bar.edit_profile": "Profil szerkesztése",
   "navigation_bar.explore": "Felfedezés",
   "navigation_bar.favourites": "Kedvencek",
   "navigation_bar.filters": "Némított szavak",
   "navigation_bar.follow_requests": "Követési kérelmek",
-  "navigation_bar.followed_tags": "Követett hashtagek",
+  "navigation_bar.followed_tags": "Követett #címkék",
   "navigation_bar.follows_and_followers": "Követettek és követők",
   "navigation_bar.lists": "Listák",
   "navigation_bar.logout": "Kijelentkezés",
@@ -395,15 +395,15 @@
   "navigation_bar.public_timeline": "Föderációs idővonal",
   "navigation_bar.search": "Keresés",
   "navigation_bar.security": "Biztonság",
-  "not_signed_in_indicator.not_signed_in": "Az erőforrás eléréséhez be kell jelentkezned.",
+  "not_signed_in_indicator.not_signed_in": "Az erőforrás eléréséhez be kell jelentkezni.",
   "notification.admin.report": "{name} jelentette: {target}",
   "notification.admin.sign_up": "{name} regisztrált",
   "notification.favourite": "{name} kedvencnek jelölte a bejegyzésedet",
-  "notification.follow": "{name} követ téged",
-  "notification.follow_request": "{name} követni szeretne téged",
-  "notification.mention": "{name} megemlített",
-  "notification.own_poll": "A szavazásod véget ért",
-  "notification.poll": "Egy szavazás, melyben részt vettél, véget ért",
+  "notification.follow": "{name} követ engem",
+  "notification.follow_request": "{name} követni szeretne engem",
+  "notification.mention": "{name} megemlített engem",
+  "notification.own_poll": "A szavazás véget ért",
+  "notification.poll": "Egy általam részt vett szavazás véget ért",
   "notification.reblog": "{name} megtolta a bejegyzésedet",
   "notification.status": "{name} bejegyzést tett közzé",
   "notification.update": "{name} szerkesztett egy bejegyzést",
@@ -413,47 +413,47 @@
   "notifications.column_settings.admin.sign_up": "Új regisztrálók:",
   "notifications.column_settings.alert": "Asztali értesítések",
   "notifications.column_settings.favourite": "Kedvencek:",
-  "notifications.column_settings.filter_bar.advanced": "Minden kategória megjelenítése",
-  "notifications.column_settings.filter_bar.category": "Gyorskereső mező",
-  "notifications.column_settings.filter_bar.show_bar": "Szűrősáv mutatása",
+  "notifications.column_settings.filter_bar.advanced": "Összes kategória megjelenítése",
+  "notifications.column_settings.filter_bar.category": "Gyorsszűrő sáv",
+  "notifications.column_settings.filter_bar.show_bar": "Szűrősáv megjelenítése",
   "notifications.column_settings.follow": "Új követők:",
   "notifications.column_settings.follow_request": "Új követési kérelmek:",
   "notifications.column_settings.mention": "Megemlítések:",
-  "notifications.column_settings.poll": "Szavazás eredménye:",
+  "notifications.column_settings.poll": "Szavazási eredmények:",
   "notifications.column_settings.push": "Push értesítések",
   "notifications.column_settings.reblog": "Megtolások:",
-  "notifications.column_settings.show": "Oszlopban mutatás",
+  "notifications.column_settings.show": "Megjelenítés oszlopban",
   "notifications.column_settings.sound": "Hang lejátszása",
   "notifications.column_settings.status": "Új bejegyzések:",
   "notifications.column_settings.unread_notifications.category": "Olvasatlan értesítések",
   "notifications.column_settings.unread_notifications.highlight": "Olvasatlan értesítések kiemelése",
   "notifications.column_settings.update": "Szerkesztések:",
-  "notifications.filter.all": "Mind",
+  "notifications.filter.all": "Összes",
   "notifications.filter.boosts": "Megtolások",
-  "notifications.filter.favourites": "Kedvencnek jelölések",
+  "notifications.filter.favourites": "Kedvencek",
   "notifications.filter.follows": "Követések",
   "notifications.filter.mentions": "Megemlítések",
   "notifications.filter.polls": "Szavazások eredményei",
   "notifications.filter.statuses": "Frissítések azoktól, akiket követsz",
   "notifications.grant_permission": "Engedély megadása.",
   "notifications.group": "{count} értesítés",
-  "notifications.mark_as_read": "Minden értesítés olvasottnak jelölése",
-  "notifications.permission_denied": "Nem tudjuk engedélyezni az asztali értesítéseket, mert az engedélyt megtagadták.",
-  "notifications.permission_denied_alert": "Az asztali értesítések nem engedélyezhetőek, mert az engedélyt megtagadták a böngészőben",
-  "notifications.permission_required": "Az asztali értesítések nem elérhetőek, mert a szükséges engedélyt nem adtad meg.",
+  "notifications.mark_as_read": "Összes értesítés megjelölése olvasottként",
+  "notifications.permission_denied": "Az asztali értesítések nem érhetők el a korábban elutasított böngésző engedély kérelem miatt",
+  "notifications.permission_denied_alert": "Az asztali értesítések nem engedélyezhetők a korábban elutasított böngésző engedély miatt",
+  "notifications.permission_required": "Az asztali értesítések nem érhetők, mivel a szükséges engedély nem lett megadva.",
   "notifications_permission_banner.enable": "Asztali értesítések engedélyezése",
-  "notifications_permission_banner.how_to_control": "Ahhoz, hogy értesítéseket kapj akkor, amikor a Mastodon nincs megnyitva, engedélyezd az asztali értesítéseket. Pontosan be tudod állítani, hogy milyen interakciókról értesülj a fenti {icon} gombon keresztül, ha egyszer már engedélyezted őket.",
-  "notifications_permission_banner.title": "Soha ne mulassz el semmit",
-  "picture_in_picture.restore": "Visszarakás",
+  "notifications_permission_banner.how_to_control": "Bezárt Mastononnál értesések fogadásához engedélyezni kell az asztali értesítéseket. Pontosan lehet vezérelni, hogy milyen interakciókról érkezzen értesítés fenti {icon} gombon keresztül, ha már lorábban megtörtént az engedélyezés.",
+  "notifications_permission_banner.title": "Soha ne mulasszunk el semmit",
+  "picture_in_picture.restore": "Visszahelyezés",
   "poll.closed": "Lezárva",
   "poll.refresh": "Frissítés",
   "poll.total_people": "{count, plural, one {# személy} other {# személy}}",
   "poll.total_votes": "{count, plural, one {# szavazat} other {# szavazat}}",
   "poll.vote": "Szavazás",
-  "poll.voted": "Erre a válaszra szavaztál",
+  "poll.voted": "Megtörtént a szavazás erre a kérdésre",
   "poll.votes": "{votes, plural, one {# szavazat} other {# szavazat}}",
   "poll_button.add_poll": "Új szavazás",
-  "poll_button.remove_poll": "Szavazás törlése",
+  "poll_button.remove_poll": "Szavazás eltávolítása",
   "privacy.change": "Bejegyzés láthatóságának módosítása",
   "privacy.direct.long": "Csak a megemlített felhasználóknak látható",
   "privacy.direct.short": "Csak megemlítetteknek",
@@ -466,7 +466,7 @@
   "privacy_policy.last_updated": "Utoljára frissítve: {date}",
   "privacy_policy.title": "Adatvédelmi szabályzat",
   "refresh": "Frissítés",
-  "regeneration_indicator.label": "Töltődik…",
+  "regeneration_indicator.label": "A betöltés folyamatban van…",
   "regeneration_indicator.sublabel": "A saját idővonalad épp készül!",
   "relative_time.days": "{number}n",
   "relative_time.full.days": "{number, plural, one {# napja} other {# napja}}",
@@ -481,7 +481,7 @@
   "relative_time.today": "ma",
   "reply_indicator.cancel": "Mégsem",
   "report.block": "Letiltás",
-  "report.block_explanation": "Nem fogod látni a bejegyzéseit. Nem fogja tudni megnézni a bejegyzéseidet és nem fog tudni követni sem. Azt is meg fogja tudni mondani, hogy letiltottad.",
+  "report.block_explanation": "A bejegyzéseik nem áthatók. Nem nézheti meg a saját bejegyzéseimet és nem tudni követni sem. Azt is meg fogja tudni mondani, hogy letiltották.",
   "report.categories.other": "Egyéb",
   "report.categories.spam": "Kéretlen üzenet",
   "report.categories.violation": "A tartalom a kiszolgáló egy vagy több szabályát sérti",
@@ -492,11 +492,11 @@
   "report.close": "Kész",
   "report.comment.title": "Van valami, amiről tudnunk kellene?",
   "report.forward": "Továbbítás: {target}",
-  "report.forward_hint": "Ez a fiók egy másik kiszolgálóról van. Oda is elküldöd a jelentés egy anonimizált másolatát?",
+  "report.forward_hint": "Ez a fiók egy másik kiszolgálóról van. Oda is elküldésre kerüljön a jelentés egy anonimizált másolata?",
   "report.mute": "Némítás",
   "report.mute_explanation": "Nem fogod látni a bejegyzéseit. Továbbra is fog tudni követni, és látni fogja a bejegyzéseidet, és nem fogja tudni, hogy némítottad.",
   "report.next": "Következő",
-  "report.placeholder": "További megjegyzések",
+  "report.placeholder": "További hozzászólások",
   "report.reasons.dislike": "Nem tetszik",
   "report.reasons.dislike_description": "Ezt nem szeretném látni",
   "report.reasons.other": "Valami más",
@@ -512,7 +512,7 @@
   "report.submit": "Küldés",
   "report.target": "{target} jelentése",
   "report.thanks.take_action": "Itt vannak a beállítások, melyek szabályozzák, hogy mit látsz a Mastodonon:",
-  "report.thanks.take_action_actionable": "Míg átnézzük, a következőket teheted @{name} ellen:",
+  "report.thanks.take_action_actionable": "Míg átnézzük, a következőket lehet tenni @{name} ellen:",
   "report.thanks.title": "Nem akarod ezt látni?",
   "report.thanks.title_actionable": "Köszönjük, hogy jelentetted, megnézzük.",
   "report.unfollow": "@{name} követésének leállítása",
@@ -523,32 +523,32 @@
   "report_notification.categories.violation": "Szabálysértés",
   "report_notification.open": "Bejelentés megnyitása",
   "search.placeholder": "Keresés",
-  "search.search_or_paste": "Keresés vagy URL beillesztése",
-  "search_popout.search_format": "Speciális keresés",
+  "search.search_or_paste": "Keresés vagy webcím beillesztése",
+  "search_popout.search_format": "Bővített keresési forma",
   "search_popout.tips.full_text": "Egyszerű szöveg, mely általad írt, kedvencnek jelölt vagy megtolt bejegyzéseket, rólad szóló megemlítéseket, felhasználói neveket, megjelenített neveket, hashtageket ad majd vissza.",
-  "search_popout.tips.hashtag": "hashtag",
+  "search_popout.tips.hashtag": "#címke",
   "search_popout.tips.status": "bejegyzés",
-  "search_popout.tips.text": "Egyszerű szöveg. Illeszkedő megjelenített nevet, felhasználói nevet, hashtageket ad majd vissza",
+  "search_popout.tips.text": "Az egyszerű szöveg illeszkedő megjelenített nevet, felhasználónevet, #címkéket ad vissza",
   "search_popout.tips.user": "felhasználó",
   "search_results.accounts": "Emberek",
   "search_results.all": "Összes",
-  "search_results.hashtags": "Hashtagek",
+  "search_results.hashtags": "#Címkék",
   "search_results.nothing_found": "Nincs találat ezekre a keresési kifejezésekre",
   "search_results.statuses": "Bejegyzések",
   "search_results.statuses_fts_disabled": "Ezen a Mastodon szerveren nem engedélyezett a bejegyzések tartalom szerinti keresése.",
   "search_results.title": "Keresés erre: {q}",
   "search_results.total": "{count, number} {count, plural, one {találat} other {találat}}",
-  "server_banner.about_active_users": "Az elmúlt 30 napban ezt a kiszolgálót használók száma (Havi aktív felhasználók)",
+  "server_banner.about_active_users": "Az elmúlt 30 napban ezt a kiszolgálót használó emberek (Havi aktív felhasználók)",
   "server_banner.active_users": "aktív felhasználó",
   "server_banner.administered_by": "Adminisztrátor:",
-  "server_banner.introduction": "{domain} része egy decentralizált közösségi hálónak, melyet a {mastodon} hajt meg.",
-  "server_banner.learn_more": "Tudj meg többet",
-  "server_banner.server_stats": "Kiszolgálóstatisztika:",
+  "server_banner.introduction": "{domain} része egy központ nélküliközösségi hálónak, melyet a {mastodon} hajt meg.",
+  "server_banner.learn_more": "További információ",
+  "server_banner.server_stats": "Szerver statisztika:",
   "sign_in_banner.create_account": "Fiók létrehozása",
   "sign_in_banner.sign_in": "Bejelentkezés",
-  "sign_in_banner.text": "Jelentkezz be profilok vagy hashtagek követéséhez, kedvencnek jelöléséhez, bejegyzések megosztásához, megválaszolásához. A fiókodból más kiszolgálókon is kommunikálhatsz.",
+  "sign_in_banner.text": "Jelentkezzünk be profilok vagy hashtagek követéséhez, kedvencnek jelöléséhez, bejegyzések megosztásához, megválaszolásához. A fiókból más kiszolgálókon is kommunikálhatunk.",
   "status.admin_account": "Moderációs felület megnyitása @{name} fiókhoz",
-  "status.admin_domain": "A következő moderációs felületének megnyitása: @{domain}",
+  "status.admin_domain": "Moderációs felület megnyitása {domain} esetében",
   "status.admin_status": "Bejegyzés megnyitása a moderációs felületen",
   "status.block": "@{name} letiltása",
   "status.bookmark": "Könyvjelzőzés",
@@ -558,6 +558,7 @@
   "status.delete": "Törlés",
   "status.detailed_status": "Részletes beszélgetési nézet",
   "status.direct": "Közvetlen üzenet @{name} számára",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Szerkesztés",
   "status.edited": "Szerkesztve: {date}",
   "status.edited_x_times": "{count, plural, one {{count} alkalommal} other {{count} alkalommal}} szerkesztve",
@@ -595,19 +596,19 @@
   "status.show_less_all": "Kevesebbet mindenhol",
   "status.show_more": "Többet",
   "status.show_more_all": "Többet mindenhol",
-  "status.show_original": "Eredeti mutatása",
+  "status.show_original": "Eredeti megjelenítése",
   "status.translate": "Fordítás",
   "status.translated_from_with": "{lang} nyelvről fordítva {provider} szolgáltatással",
   "status.uncached_media_warning": "Nem érhető el",
   "status.unmute_conversation": "Beszélgetés némításának feloldása",
   "status.unpin": "Kitűzés eltávolítása a profilodról",
-  "subscribed_languages.lead": "A változtatás után csak a kiválasztott nyelvű bejegyzések fognak megjelenni a kezdőlapon és az idővonalakon. Ha egy sincs kiválasztva, akkor minden nyelven megjelennek a bejegyzések.",
+  "subscribed_languages.lead": "A változtatás után csak a kiválasztott nyelvű bejegyzések fognak megjelenni a kezdőoldalon és az idővonalakon. Ha egy sincs kiválasztva, akkor az összes nyelvű bejegyzések megjelennek.",
   "subscribed_languages.save": "Változások mentése",
-  "subscribed_languages.target": "Feliratkozott nyelvek módosítása a következőnél: {target}",
+  "subscribed_languages.target": "Feliratkozott nyelvek módosítása {target} esetében",
   "suggestions.dismiss": "Javaslat elvetése",
-  "suggestions.header": "Esetleg érdekelhet…",
-  "tabs_bar.federated_timeline": "Föderációs",
-  "tabs_bar.home": "Kezdőlap",
+  "suggestions.header": "Esetleg érdeklődésre tarthat számot…",
+  "tabs_bar.federated_timeline": "Összekapcsolt",
+  "tabs_bar.home": "Kezdőoldal",
   "tabs_bar.local_timeline": "Helyi",
   "tabs_bar.notifications": "Értesítések",
   "time_remaining.days": "{number, plural, one {# nap} other {# nap}} van hátra",
@@ -621,29 +622,29 @@
   "timeline_hint.resources.statuses": "Régi bejegyzések",
   "trends.counter_by_accounts": "{count, plural, one {{counter} ember} other {{counter} ember}} az elmúlt {days, plural,one {napban} other {{days} napban}}",
   "trends.trending_now": "Most felkapott",
-  "ui.beforeunload": "A piszkozatod el fog veszni, ha elhagyod a Mastodont.",
+  "ui.beforeunload": "A vázlat elveszik a Mastodon elhagyásakor.",
   "units.short.billion": "{count}Mrd",
   "units.short.million": "{count}M",
   "units.short.thousand": "{count}K",
-  "upload_area.title": "Húzd ide a feltöltéshez",
-  "upload_button.label": "Média hozzáadása",
-  "upload_error.limit": "Túllépted a fájlfeltöltési korlátot.",
+  "upload_area.title": "Húzás a feltöltéshez",
+  "upload_button.label": "Képek, videó vagy audió fájl hozzáadása",
+  "upload_error.limit": "A fájlfeltöltési korlát elérésre került.",
   "upload_error.poll": "Szavazásnál nem lehet fájlt feltölteni.",
-  "upload_form.audio_description": "Írja le a hallássérültek számára",
-  "upload_form.description": "Leírás látáskorlátozottak számára",
+  "upload_form.audio_description": "Leírás be a siket vagy hallássérült embereknek",
+  "upload_form.description": "Leírás be vak vagy gyengénlátó embereknek",
   "upload_form.description_missing": "Nincs leírás megadva",
   "upload_form.edit": "Szerkesztés",
-  "upload_form.thumbnail": "Előnézet megváltoztatása",
+  "upload_form.thumbnail": "Bélyegkép megváltoztatása",
   "upload_form.undo": "Törlés",
-  "upload_form.video_description": "Írja le a hallás- vagy látássérültek számára",
+  "upload_form.video_description": "Leírás be a siket, hallássérült, vak vagy gyengénlátó embereknek",
   "upload_modal.analyzing_picture": "Kép elemzése…",
-  "upload_modal.apply": "Alkalmaz",
+  "upload_modal.apply": "Alkalmazás",
   "upload_modal.applying": "Alkalmazás…",
   "upload_modal.choose_image": "Kép kiválasztása",
   "upload_modal.description_placeholder": "A gyors, barna róka átugrik a lusta kutya fölött",
   "upload_modal.detect_text": "Szöveg felismerése a képről",
   "upload_modal.edit_media": "Média szerkesztése",
-  "upload_modal.hint": "Kattints vagy húzd a kört az előnézetben arra a fókuszpontra, mely minden megjelenített bélyegképen látható kell, legyen.",
+  "upload_modal.hint": "Kattintás vagy kör húzása az előnézetben arra a fókuszpontra, mely minden megjelenített bélyegképen láthatónak kell lenni.",
   "upload_modal.preparing_ocr": "OCR előkészítése…",
   "upload_modal.preview_label": "Előnézet ({ratio})",
   "upload_progress.label": "Feltöltés...",
diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json
index 87ecee646..35c94f829 100644
--- a/app/javascript/mastodon/locales/hy.json
+++ b/app/javascript/mastodon/locales/hy.json
@@ -558,6 +558,7 @@
   "status.delete": "Ջնջել",
   "status.detailed_status": "Շղթայի ընդլայնուած դիտում",
   "status.direct": "Նամակ գրել {name} -ին",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Խմբագրել",
   "status.edited": "Խմբագրուել է՝ {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json
index 1f93219c8..038013448 100644
--- a/app/javascript/mastodon/locales/id.json
+++ b/app/javascript/mastodon/locales/id.json
@@ -558,6 +558,7 @@
   "status.delete": "Hapus",
   "status.detailed_status": "Tampilan detail percakapan",
   "status.direct": "Pesan langsung @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Diedit {date}",
   "status.edited_x_times": "Diedit {count, plural, other {{count} kali}}",
diff --git a/app/javascript/mastodon/locales/ig.json b/app/javascript/mastodon/locales/ig.json
index 3d2cd7772..830f0a19d 100644
--- a/app/javascript/mastodon/locales/ig.json
+++ b/app/javascript/mastodon/locales/ig.json
@@ -558,6 +558,7 @@
   "status.delete": "Hichapụ",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json
index b6f61d23b..00ae025f8 100644
--- a/app/javascript/mastodon/locales/io.json
+++ b/app/javascript/mastodon/locales/io.json
@@ -558,6 +558,7 @@
   "status.delete": "Efacar",
   "status.detailed_status": "Detala konversvido",
   "status.direct": "Direta mesajigez @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Modifikez",
   "status.edited": "Modifikesis ye {date}",
   "status.edited_x_times": "Modifikesis {count, plural, one {{count} foyo} other {{count} foyi}}",
diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json
index 3611b26ed..50e20aded 100644
--- a/app/javascript/mastodon/locales/is.json
+++ b/app/javascript/mastodon/locales/is.json
@@ -558,6 +558,7 @@
   "status.delete": "Eyða",
   "status.detailed_status": "Nákvæm spjallþráðasýn",
   "status.direct": "Bein skilaboð @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Breyta",
   "status.edited": "Breytt {date}",
   "status.edited_x_times": "Breytt {count, plural, one {{count} sinni} other {{count} sinnum}}",
diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json
index ce8f45fab..108cd73c8 100644
--- a/app/javascript/mastodon/locales/it.json
+++ b/app/javascript/mastodon/locales/it.json
@@ -558,6 +558,7 @@
   "status.delete": "Elimina",
   "status.detailed_status": "Vista conversazione dettagliata",
   "status.direct": "Messaggio diretto a @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Modifica",
   "status.edited": "Modificato il {date}",
   "status.edited_x_times": "Modificato {count, plural, one {{count} volta} other {{count} volte}}",
diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json
index 172da89ce..7e56d3760 100644
--- a/app/javascript/mastodon/locales/ja.json
+++ b/app/javascript/mastodon/locales/ja.json
@@ -562,6 +562,7 @@
   "status.delete": "削除",
   "status.detailed_status": "詳細な会話ビュー",
   "status.direct": "@{name}さんにダイレクトメッセージ",
+  "status.direct_indicator": "Private mention",
   "status.edit": "編集",
   "status.edited": "{date}に編集",
   "status.edited_x_times": "{count}回編集",
diff --git a/app/javascript/mastodon/locales/ka.json b/app/javascript/mastodon/locales/ka.json
index 9ebdfc989..b6829f5de 100644
--- a/app/javascript/mastodon/locales/ka.json
+++ b/app/javascript/mastodon/locales/ka.json
@@ -558,6 +558,7 @@
   "status.delete": "წაშლა",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "პირდაპირი წერილი @{name}-ს",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/kab.json b/app/javascript/mastodon/locales/kab.json
index cf02895c5..439da7ce9 100644
--- a/app/javascript/mastodon/locales/kab.json
+++ b/app/javascript/mastodon/locales/kab.json
@@ -558,6 +558,7 @@
   "status.delete": "Kkes",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Izen usrid i @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Ẓreg",
   "status.edited": "Tettwaẓreg deg {date}",
   "status.edited_x_times": "Tettwaẓreg {count, plural, one {{count} n tikkelt} other {{count} n tikkal}}",
diff --git a/app/javascript/mastodon/locales/kk.json b/app/javascript/mastodon/locales/kk.json
index 6a9195c4b..f360fa209 100644
--- a/app/javascript/mastodon/locales/kk.json
+++ b/app/javascript/mastodon/locales/kk.json
@@ -558,6 +558,7 @@
   "status.delete": "Өшіру",
   "status.detailed_status": "Толық пікірталас көрінісі",
   "status.direct": "Хат жіберу @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/kn.json b/app/javascript/mastodon/locales/kn.json
index f81a47fa9..b89b4a31d 100644
--- a/app/javascript/mastodon/locales/kn.json
+++ b/app/javascript/mastodon/locales/kn.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json
index babd3b1dd..edb96f116 100644
--- a/app/javascript/mastodon/locales/ko.json
+++ b/app/javascript/mastodon/locales/ko.json
@@ -71,7 +71,7 @@
   "admin.dashboard.monthly_retention": "가입 후 월별 사용자 유지율",
   "admin.dashboard.retention.average": "평균",
   "admin.dashboard.retention.cohort": "가입한 달",
-  "admin.dashboard.retention.cohort_size": "새로운 사용자",
+  "admin.dashboard.retention.cohort_size": "새 사용자",
   "alert.rate_limited.message": "{retry_time, time, medium}에 다시 시도해 주세요.",
   "alert.rate_limited.title": "빈도 제한됨",
   "alert.unexpected.message": "예상하지 못한 에러가 발생했습니다.",
@@ -558,6 +558,7 @@
   "status.delete": "삭제",
   "status.detailed_status": "대화 자세히 보기",
   "status.direct": "@{name}에게 다이렉트 메시지",
+  "status.direct_indicator": "Private mention",
   "status.edit": "수정",
   "status.edited": "{date}에 편집됨",
   "status.edited_x_times": "{count}번 수정됨",
diff --git a/app/javascript/mastodon/locales/ku.json b/app/javascript/mastodon/locales/ku.json
index c927d9658..5fb8aff26 100644
--- a/app/javascript/mastodon/locales/ku.json
+++ b/app/javascript/mastodon/locales/ku.json
@@ -558,6 +558,7 @@
   "status.delete": "Jê bibe",
   "status.detailed_status": "Dîtina axaftina berfireh",
   "status.direct": "Peyama rasterast @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Serrast bike",
   "status.edited": "Di {date} de hate serrastkirin",
   "status.edited_x_times": "{count, plural, one {{count} car} other {{count} car}} hate serrastkirin",
diff --git a/app/javascript/mastodon/locales/kw.json b/app/javascript/mastodon/locales/kw.json
index 9a20c2ebf..c1f25017d 100644
--- a/app/javascript/mastodon/locales/kw.json
+++ b/app/javascript/mastodon/locales/kw.json
@@ -558,6 +558,7 @@
   "status.delete": "Dilea",
   "status.detailed_status": "Gwel kesklapp a-vanyl",
   "status.direct": "Messach didro dhe @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/la.json b/app/javascript/mastodon/locales/la.json
index 173ca7c61..84a8bc560 100644
--- a/app/javascript/mastodon/locales/la.json
+++ b/app/javascript/mastodon/locales/la.json
@@ -558,6 +558,7 @@
   "status.delete": "Oblitterare",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Recolere",
   "status.edited": "Recultum {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json
index fd4bbdd95..99e669c95 100644
--- a/app/javascript/mastodon/locales/lt.json
+++ b/app/javascript/mastodon/locales/lt.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json
index 9a3e3219e..2f1f70f14 100644
--- a/app/javascript/mastodon/locales/lv.json
+++ b/app/javascript/mastodon/locales/lv.json
@@ -558,6 +558,7 @@
   "status.delete": "Dzēst",
   "status.detailed_status": "Detalizēts sarunas skats",
   "status.direct": "Privāta ziņa @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Rediģēt",
   "status.edited": "Rediģēts {date}",
   "status.edited_x_times": "Rediģēts {count, plural, one {{count} reizi} other {{count} reizes}}",
diff --git a/app/javascript/mastodon/locales/mk.json b/app/javascript/mastodon/locales/mk.json
index a84cc9168..138d17b9d 100644
--- a/app/javascript/mastodon/locales/mk.json
+++ b/app/javascript/mastodon/locales/mk.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/ml.json b/app/javascript/mastodon/locales/ml.json
index 5d29c55d0..6c4975de2 100644
--- a/app/javascript/mastodon/locales/ml.json
+++ b/app/javascript/mastodon/locales/ml.json
@@ -558,6 +558,7 @@
   "status.delete": "മായ്ക്കുക",
   "status.detailed_status": "വിശദമായ സംഭാഷണ കാഴ്‌ച",
   "status.direct": "@{name} ന് നേരിട്ട് മെസേജ് അയക്കുക",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/mr.json b/app/javascript/mastodon/locales/mr.json
index 680aea770..9388f4792 100644
--- a/app/javascript/mastodon/locales/mr.json
+++ b/app/javascript/mastodon/locales/mr.json
@@ -54,7 +54,7 @@
   "account.posts_with_replies": "Toots and replies",
   "account.report": "@{name} ची तक्रार करा",
   "account.requested": "Awaiting approval",
-  "account.requested_follow": "{name} has requested to follow you",
+  "account.requested_follow": "{name} ने आपल्याला फॉलो करण्याची रिक्वेस्ट केली आहे",
   "account.share": "@{name} चे प्रोफाइल शेअर करा",
   "account.show_reblogs": "{name}चे सर्व बुस्ट्स दाखवा",
   "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}",
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/ms.json b/app/javascript/mastodon/locales/ms.json
index 3a374884b..317efb4c4 100644
--- a/app/javascript/mastodon/locales/ms.json
+++ b/app/javascript/mastodon/locales/ms.json
@@ -558,6 +558,7 @@
   "status.delete": "Padam",
   "status.detailed_status": "Paparan perbualan terperinci",
   "status.direct": "Mesej terus @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Sunting",
   "status.edited": "Disunting {date}",
   "status.edited_x_times": "Disunting {count, plural, other {{count} kali}}",
diff --git a/app/javascript/mastodon/locales/my.json b/app/javascript/mastodon/locales/my.json
index cf5b3406c..502e8ca97 100644
--- a/app/javascript/mastodon/locales/my.json
+++ b/app/javascript/mastodon/locales/my.json
@@ -194,7 +194,7 @@
   "dismissable_banner.explore_tags": "ဤ hashtag များသည် ယခုအချိန်တွင် ဗဟိုချုပ်ကိုင်မှုလျှော့ချထားသော ကွန်ရက်၏ အခြားဆာဗာများပေါ်ရှိ လူများကြားတွင် ဆွဲဆောင်မှုရှိလာပါသည်",
   "dismissable_banner.public_timeline": "ဗဟိုချုပ်ကိုင်မှုလျှော့ချထားသော ဤဆာဗာနှင့် အခြားဆာဗာတို့တွင် တင်ထားသည့် လတ်တလော အများမြင်ပို့စ်များဖြစ်သည်။",
   "embed.instructions": "Embed this status on your website by copying the code below.",
-  "embed.preview": "Here is what it will look like:",
+  "embed.preview": "ဒါမျိုးမြင်ရမှာပါ။",
   "emoji_button.activity": "လုပ်ဆောင်ချက်",
   "emoji_button.clear": "ရှင်းလင်းမည်",
   "emoji_button.custom": "စိတ်ကြိုက်",
@@ -265,7 +265,7 @@
   "follow_recommendations.lead": "သင်စောင့်ကြည့်ထားသူများ၏ ပို့စ်များမှာ သင့်ပင်မစာမျက်နှာတွင် အချိန်နှင့်တပြေးညီ ပေါ်လာပါမည်။ မကြောက်ပါနှင့်။ အချိန်မရွေး စောင့်ကြည့်ခြင်းအား ရပ်တန့်နိုင်ပါသည်။",
   "follow_request.authorize": "လုပ်ပိုင်ခွင့်",
   "follow_request.reject": "ဖယ်ရှားပါ",
-  "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "follow_requests.unlocked_explanation": "သင့်အကောင့်ကို လော့ခ်ချမထားသော်လည်း၊ {domain} ဝန်ထမ်းများသည် ဤအကောင့်များမှ လိုက်နာရန်တောင်းဆိုမှုများကို ကိုယ်တိုင်ပြန်လည်စစ်ဆေးလိုမည်ဟု ထင်မြင်ယူဆပါသည်။",
   "followed_tags": "Hashtag ကို စောင့်ကြည့်ပါမည်",
   "footer.about": "အကြောင်း",
   "footer.directory": "ပရိုဖိုင်များလမ်းညွှန်",
@@ -414,7 +414,7 @@
   "notifications.column_settings.alert": "Desktop သတိပေးချက်များ",
   "notifications.column_settings.favourite": "ကြိုက်နှစ်သက်မှုများ",
   "notifications.column_settings.filter_bar.advanced": "ခေါင်းစဥ်အားလုံးများကိုဖော်ပြပါ",
-  "notifications.column_settings.filter_bar.category": "Quick filter bar",
+  "notifications.column_settings.filter_bar.category": "အမြန်စစ်ထုတ်မှုဘား",
   "notifications.column_settings.filter_bar.show_bar": "စစ်ထုတ်မှုဘားကို ပြပါ",
   "notifications.column_settings.follow": "စောင့်ကြည့်သူအသစ်များ -",
   "notifications.column_settings.follow_request": "စောင့်ကြည့်ရန် တောင်းဆိုမှုအသစ်များ -",
@@ -442,7 +442,7 @@
   "notifications.permission_denied_alert": "ဘရောက်ဆာခွင့်ပြုချက်ကို ငြင်းပယ်ခဲ့သောကြောင့် ဒက်စ်တော့ အသိပေးချက်များကို ဖွင့်၍မရပါ",
   "notifications.permission_required": "လိုအပ်သောခွင့်ပြုချက်ကို မပေးထားသောကြောင့် ဒက်စ်တော့ အသိပေးချက်များကို မရရှိနိုင်ပါ။",
   "notifications_permission_banner.enable": "ဒက်စ်တော့ အသိပေးချက်များကို ဖွင့်ပါ",
-  "notifications_permission_banner.how_to_control": "To receive notifications when Mastodon isn't open, enable desktop notifications. You can control precisely which types of interactions generate desktop notifications through the {icon} button above once they're enabled.",
+  "notifications_permission_banner.how_to_control": "Mastodon မဖွင့်သည့်အခါ အကြောင်းကြားချက်များကို လက်ခံရယူရန်၊ ဒက်စ်တော့ အသိပေးချက်များကို ဖွင့်ပါ။ ၎င်းတို့ကို ဖွင့်ပြီးသည်နှင့် ၎င်းတို့ကို ဖွင့်ပြီးသည်နှင့် အထက် {icon} ခလုတ်မှ ဒက်စ်တော့ အသိပေးချက်များကို ထုတ်ပေးသည့် အပြန်အလှန်တုံ့ပြန်မှု အမျိုးအစားများကို သင် အတိအကျ ထိန်းချုပ်နိုင်သည်။",
   "notifications_permission_banner.title": "လက်လွတ်မခံပါနှင့်",
   "picture_in_picture.restore": "ပြန်ထားပါ",
   "poll.closed": "ပိတ်သွားပြီ",
@@ -528,7 +528,7 @@
   "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
   "search_popout.tips.hashtag": "ဟက်ရှ်တက်ခ်",
   "search_popout.tips.status": "ပို့စ်",
-  "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
+  "search_popout.tips.text": "ရိုးရှင်းသော စာသားသည် ကိုက်ညီသည့် ဖော်ပြအမည်များ၊ အသုံးပြုသူအမည်များနှင့် hashtag များကို ပြန်ဖော်ပြပေးသည်။\n",
   "search_popout.tips.user": "အသုံးပြုသူ",
   "search_results.accounts": "လူပုဂ္ဂိုလ်",
   "search_results.all": "အားလုံး",
@@ -558,6 +558,7 @@
   "status.delete": "ဖျက်ရန်",
   "status.detailed_status": "အသေးစိတ်စကားပြောဆိုမှုမြင်ကွင်း",
   "status.direct": "@{name} ကို တိုက်ရိုက်စာပို့မည်",
+  "status.direct_indicator": "Private mention",
   "status.edit": "ပြင်ဆင်ရန်",
   "status.edited": "{date} ကို ပြင်ဆင်ပြီးပါပြီ",
   "status.edited_x_times": "{count, plural, one {{count} time} other {{count} times}} ပြင်ဆင်ခဲ့သည်",
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index c2679ddef..359a1f200 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -558,6 +558,7 @@
   "status.delete": "Verwijderen",
   "status.detailed_status": "Uitgebreide gespreksweergave",
   "status.direct": "@{name} een direct bericht sturen",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Bewerken",
   "status.edited": "Bewerkt op {date}",
   "status.edited_x_times": "{count, plural, one {{count} keer} other {{count} keer}} bewerkt",
diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json
index 76b1ebf2a..d3b74204a 100644
--- a/app/javascript/mastodon/locales/nn.json
+++ b/app/javascript/mastodon/locales/nn.json
@@ -558,6 +558,7 @@
   "status.delete": "Slett",
   "status.detailed_status": "Detaljert samtalevisning",
   "status.direct": "Send melding til @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Rediger",
   "status.edited": "Redigert {date}",
   "status.edited_x_times": "Redigert {count, plural, one {{count} gong} other {{count} gonger}}",
diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json
index 372902e8b..5b0821946 100644
--- a/app/javascript/mastodon/locales/no.json
+++ b/app/javascript/mastodon/locales/no.json
@@ -558,6 +558,7 @@
   "status.delete": "Slett",
   "status.detailed_status": "Detaljert samtalevisning",
   "status.direct": "Send direktemelding til @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Rediger",
   "status.edited": "Redigert {date}",
   "status.edited_x_times": "Redigert {count, plural,one {{count} gang} other {{count} ganger}}",
diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index cde922c05..255d6f276 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -558,6 +558,7 @@
   "status.delete": "Escafar",
   "status.detailed_status": "Vista detalhada de la convèrsa",
   "status.direct": "Messatge per @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Modificar",
   "status.edited": "Modificat {date}",
   "status.edited_x_times": "Modificat {count, plural, un {{count} còp} other {{count} còps}}",
diff --git a/app/javascript/mastodon/locales/pa.json b/app/javascript/mastodon/locales/pa.json
index b81bb1356..ee4d3a7f9 100644
--- a/app/javascript/mastodon/locales/pa.json
+++ b/app/javascript/mastodon/locales/pa.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json
index 4dadec0c0..c72820c3e 100644
--- a/app/javascript/mastodon/locales/pl.json
+++ b/app/javascript/mastodon/locales/pl.json
@@ -563,6 +563,7 @@
   "status.delete": "Usuń",
   "status.detailed_status": "Szczegółowy widok konwersacji",
   "status.direct": "Wyślij wiadomość bezpośrednią do @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edytuj",
   "status.edited": "Edytowano {date}",
   "status.edited_x_times": "Edytowano {count, plural, one {{count} raz} other {{count} razy}}",
diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json
index 249787438..8913f21e5 100644
--- a/app/javascript/mastodon/locales/pt-BR.json
+++ b/app/javascript/mastodon/locales/pt-BR.json
@@ -558,6 +558,7 @@
   "status.delete": "Excluir",
   "status.detailed_status": "Visão detalhada da conversa",
   "status.direct": "Enviar toot direto para @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Editar",
   "status.edited": "Editado em {date}",
   "status.edited_x_times": "Editado {count, plural, one {{count} hora} other {{count} vezes}}",
diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json
index d72c5c700..99f9c156d 100644
--- a/app/javascript/mastodon/locales/pt-PT.json
+++ b/app/javascript/mastodon/locales/pt-PT.json
@@ -558,6 +558,7 @@
   "status.delete": "Eliminar",
   "status.detailed_status": "Vista pormenorizada da conversa",
   "status.direct": "Mensagem direta @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Editar",
   "status.edited": "Editado em {date}",
   "status.edited_x_times": "Editado {count, plural,one {{count} vez} other {{count} vezes}}",
diff --git a/app/javascript/mastodon/locales/ro.json b/app/javascript/mastodon/locales/ro.json
index c50a6a12b..0c331b425 100644
--- a/app/javascript/mastodon/locales/ro.json
+++ b/app/javascript/mastodon/locales/ro.json
@@ -558,6 +558,7 @@
   "status.delete": "Șterge",
   "status.detailed_status": "Conversația detaliată",
   "status.direct": "Mesaj direct către @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Modifică",
   "status.edited": "Modificat în data de {date}",
   "status.edited_x_times": "Modificată {count, plural, one {o dată} few {de {count} ori} other {de {count} de ori}}",
diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json
index 93a39c380..8820a67e6 100644
--- a/app/javascript/mastodon/locales/ru.json
+++ b/app/javascript/mastodon/locales/ru.json
@@ -163,7 +163,7 @@
   "confirmations.domain_block.confirm": "Да, заблокировать узел",
   "confirmations.domain_block.message": "Вы точно уверены, что хотите заблокировать {domain} полностью? В большинстве случаев нескольких блокировок и игнорирований вполне достаточно. Вы перестанете видеть публичную ленту и уведомления оттуда. Ваши подписчики из этого домена будут удалены.",
   "confirmations.edit.confirm": "Редактировать",
-  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
+  "confirmations.edit.message": "В данный момент, редактирование перезапишет составляемое вами сообщение. Вы уверены, что хотите продолжить?",
   "confirmations.logout.confirm": "Выйти",
   "confirmations.logout.message": "Вы уверены, что хотите выйти?",
   "confirmations.mute.confirm": "Игнорировать",
@@ -558,6 +558,7 @@
   "status.delete": "Удалить",
   "status.detailed_status": "Подробный просмотр обсуждения",
   "status.direct": "Написать @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Изменить",
   "status.edited": "Последнее изменение: {date}",
   "status.edited_x_times": "{count, plural, one {{count} изменение} many {{count} изменений} other {{count} изменения}}",
diff --git a/app/javascript/mastodon/locales/sa.json b/app/javascript/mastodon/locales/sa.json
index 13e5cbb57..15c6fc02e 100644
--- a/app/javascript/mastodon/locales/sa.json
+++ b/app/javascript/mastodon/locales/sa.json
@@ -558,6 +558,7 @@
   "status.delete": "मार्जय",
   "status.detailed_status": "विस्तृतसंभाषणदृश्यम्",
   "status.direct": "प्रत्यक्षसन्देशः @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "सम्पादय",
   "status.edited": "सम्पादितं {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} वारम्} other {{count} वारम्}}",
diff --git a/app/javascript/mastodon/locales/sc.json b/app/javascript/mastodon/locales/sc.json
index 38aa39f3f..b61ac537c 100644
--- a/app/javascript/mastodon/locales/sc.json
+++ b/app/javascript/mastodon/locales/sc.json
@@ -558,6 +558,7 @@
   "status.delete": "Cantzella",
   "status.detailed_status": "Visualizatzione de detàlliu de arresonada",
   "status.direct": "Messàgiu deretu a @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/sco.json b/app/javascript/mastodon/locales/sco.json
index 1abcac47e..f32d8d2e7 100644
--- a/app/javascript/mastodon/locales/sco.json
+++ b/app/javascript/mastodon/locales/sco.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailt conversation view",
   "status.direct": "Direck message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Editit {date}",
   "status.edited_x_times": "Editit {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/si.json b/app/javascript/mastodon/locales/si.json
index f13aca561..3e96be5fe 100644
--- a/app/javascript/mastodon/locales/si.json
+++ b/app/javascript/mastodon/locales/si.json
@@ -558,6 +558,7 @@
   "status.delete": "මකන්න",
   "status.detailed_status": "විස්තරාත්මක සංවාද දැක්ම",
   "status.direct": "@{name} සෘජු පණිවිඩයක්",
+  "status.direct_indicator": "Private mention",
   "status.edit": "සංස්කරණය",
   "status.edited": "සංශෝධිතයි {date}",
   "status.edited_x_times": "සංශෝධිතයි {count, plural, one {වාර {count}} other {වාර {count}}}",
diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json
index a2c35fae9..e2f558b9b 100644
--- a/app/javascript/mastodon/locales/sk.json
+++ b/app/javascript/mastodon/locales/sk.json
@@ -558,6 +558,7 @@
   "status.delete": "Zmazať",
   "status.detailed_status": "Podrobný náhľad celej konverzácie",
   "status.direct": "Priama správa pre @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Uprav",
   "status.edited": "Upravené {date}",
   "status.edited_x_times": "Upravený {count, plural, one {{count} krát} other {{count} krát}}",
@@ -597,7 +598,7 @@
   "status.show_more_all": "Všetkým ukáž viac",
   "status.show_original": "Ukáž pôvodný",
   "status.translate": "Preložiť",
-  "status.translated_from_with": "Translated from {lang} using {provider}",
+  "status.translated_from_with": "Preložené z {lang} pomocou {provider}",
   "status.uncached_media_warning": "Nedostupný/é",
   "status.unmute_conversation": "Prestaň si nevšímať konverzáciu",
   "status.unpin": "Odopni z profilu",
diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json
index 331d9aa20..78502dd07 100644
--- a/app/javascript/mastodon/locales/sl.json
+++ b/app/javascript/mastodon/locales/sl.json
@@ -558,6 +558,7 @@
   "status.delete": "Izbriši",
   "status.detailed_status": "Podroben pogled pogovora",
   "status.direct": "Neposredno sporočilo @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Uredi",
   "status.edited": "Urejeno {date}",
   "status.edited_x_times": "Urejeno {count, plural, one {#-krat} two {#-krat} few {#-krat} other {#-krat}}",
diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json
index 2c4c8cd89..7322b12ae 100644
--- a/app/javascript/mastodon/locales/sq.json
+++ b/app/javascript/mastodon/locales/sq.json
@@ -558,6 +558,7 @@
   "status.delete": "Fshije",
   "status.detailed_status": "Pamje e hollësishme bisede",
   "status.direct": "Mesazh i drejtpërdrejtë për @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Përpunojeni",
   "status.edited": "Përpunuar më {date}",
   "status.edited_x_times": "Përpunuar {count, plural, one {{count} herë} other {{count} herë}}",
diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json
index 66dd80b90..904396610 100644
--- a/app/javascript/mastodon/locales/sr-Latn.json
+++ b/app/javascript/mastodon/locales/sr-Latn.json
@@ -540,7 +540,7 @@
   "search_results.total": "{count, number} {count, plural, one {rezultat} few {rezultata} other {rezultata}}",
   "server_banner.about_active_users": "Ljudi koji su koristili ovaj server u prethodnih 30 dana (mesečno aktivnih korisnika)",
   "server_banner.active_users": "aktivnih korisnika",
-  "server_banner.administered_by": "Administrirano od strane:",
+  "server_banner.administered_by": "Administrira:",
   "server_banner.introduction": "{domain} je deo decentralizovane društvene mreže koju pokreće {mastodon}.",
   "server_banner.learn_more": "Saznajte više",
   "server_banner.server_stats": "Statistike servera:",
@@ -558,6 +558,7 @@
   "status.delete": "Izbriši",
   "status.detailed_status": "Detaljan prikaz razgovora",
   "status.direct": "Pošalji poruku @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Uredi",
   "status.edited": "Uređeno {date}",
   "status.edited_x_times": "Uređeno {count, plural, one {{count} put} other {{count} puta}}",
diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json
index 28d6126bb..665d561c6 100644
--- a/app/javascript/mastodon/locales/sr.json
+++ b/app/javascript/mastodon/locales/sr.json
@@ -155,7 +155,7 @@
   "confirmations.cancel_follow_request.confirm": "Повуци захтев",
   "confirmations.cancel_follow_request.message": "Да ли сте сигурни да желите да повучете захтев да пратите {name}?",
   "confirmations.delete.confirm": "Избриши",
-  "confirmations.delete.message": "Да ли сте сигурни да желите изришете ову објаву?",
+  "confirmations.delete.message": "Да ли сте сигурни да желите да избришете ову објаву?",
   "confirmations.delete_list.confirm": "Избриши",
   "confirmations.delete_list.message": "Да ли сте сигурни да желите да трајно избришете ову листу?",
   "confirmations.discard_edit_media.confirm": "Одбаци",
@@ -167,12 +167,12 @@
   "confirmations.logout.confirm": "Одјава",
   "confirmations.logout.message": "Да ли сте сигурни да желите да се одјавите?",
   "confirmations.mute.confirm": "Игнориши",
-  "confirmations.mute.explanation": "Ово ће сакрити објаве од корисника и објаве које га помињу, али ће му и даље бити дозвољено да види ваше објаве и да вас прати.",
+  "confirmations.mute.explanation": "Ово ће сакрити објаве корисника и објаве које га помињу, али ће му и даље бити дозвољено да види Ваше објаве и да Вас прати.",
   "confirmations.mute.message": "Да ли стварно желите да игноришете корисника {name}?",
   "confirmations.redraft.confirm": "Избриши и преправи",
   "confirmations.redraft.message": "Да ли сте сигурни да желите да избришете ову објаву и да је преправите? Подржавања и ознаке као омиљених ће бити изгубљени, а одговори ће остати без оригиналне објаве.",
   "confirmations.reply.confirm": "Одговори",
-  "confirmations.reply.message": "Одговарањем ћете обрисати поруку коју састављате. Да ли сигурни да желите да наставите?",
+  "confirmations.reply.message": "Одговарањем ћете обрисати поруку коју састављате. Да ли сте сигурни да желите да наставите?",
   "confirmations.unfollow.confirm": "Отпрати",
   "confirmations.unfollow.message": "Да ли сте сигурни да желите да отпратите корисника {name}?",
   "conversation.delete": "Избриши разговор",
@@ -181,7 +181,7 @@
   "conversation.with": "Са {names}",
   "copypaste.copied": "Копирано",
   "copypaste.copy": "Копирај",
-  "directory.federated": "Са знаног фидеверзума",
+  "directory.federated": "Са знаног федиверзума",
   "directory.local": "Само са {domain}",
   "directory.new_arrivals": "Новопридошли",
   "directory.recently_active": "Недавно активни",
@@ -337,7 +337,7 @@
   "keyboard_shortcuts.search": "Фокусирај траку претраге",
   "keyboard_shortcuts.spoilers": "Прикажи/сакриј поље текста упозорења о садржају (CW)",
   "keyboard_shortcuts.start": "Отвори колону „први кораци”",
-  "keyboard_shortcuts.toggle_hidden": "Прикажи/сакриј текст иза CW-а",
+  "keyboard_shortcuts.toggle_hidden": "Прикажи/сакриј текст иза упозорења",
   "keyboard_shortcuts.toggle_sensitivity": "Прикажи/сакриј мултимедију",
   "keyboard_shortcuts.toot": "Започни нову објаву",
   "keyboard_shortcuts.unfocus": "Уклони фокус са поља за унос текста/претраге",
@@ -366,7 +366,7 @@
   "loading_indicator.label": "Учитавање...",
   "media_gallery.toggle_visible": "{number, plural, one {Сакриј слику} few {Сакриј слике} other {Сакриј слике}}",
   "missing_indicator.label": "Није пронађено",
-  "missing_indicator.sublabel": "Овај ресурс није могуће пронаћи",
+  "missing_indicator.sublabel": "Овај ресурс није било могуће пронаћи",
   "moved_to_account_banner.text": "Ваш налог {disabledAccount} је тренутно онемогућен јер сте прешли на {movedToAccount}.",
   "mute_modal.duration": "Трајање",
   "mute_modal.hide_notifications": "Сакрити обавештења од овог корисника?",
@@ -525,10 +525,10 @@
   "search.placeholder": "Претрага",
   "search.search_or_paste": "Претражите или унесите адресу",
   "search_popout.search_format": "Напредни формат претраге",
-  "search_popout.tips.full_text": "Једноставан текст враћа објаве које сте написали, додали у омиљене, подржали или у којима сте били поменути, као и подударање корисничких имена, приказана имена и хеш ознаке.",
+  "search_popout.tips.full_text": "Једноставан текст враћа објаве које сте написали, додали у омиљене, подржали или у којима сте били поменути, као и одговарајућа корисничка имена, имена за приказ и хеш ознаке.",
   "search_popout.tips.hashtag": "хеш ознака",
   "search_popout.tips.status": "објава",
-  "search_popout.tips.text": "Једноставан текст враћа подударање имена за приказ, корисничка имена и хеш ознаке",
+  "search_popout.tips.text": "Једноставан текст враћа подударна имена за приказ, корисничка имена и хеш ознаке",
   "search_popout.tips.user": "корисник",
   "search_results.accounts": "Људи",
   "search_results.all": "Све",
@@ -540,7 +540,7 @@
   "search_results.total": "{count, number} {count, plural, one {резултат} few {резултата} other {резултата}}",
   "server_banner.about_active_users": "Људи који су користили овај сервер у претходних 30 дана (месечно активних корисника)",
   "server_banner.active_users": "активних корисника",
-  "server_banner.administered_by": "Администрирано од стране:",
+  "server_banner.administered_by": "Администрира:",
   "server_banner.introduction": "{domain} је део децентрализоване друштвене мреже коју покреће {mastodon}.",
   "server_banner.learn_more": "Сазнајте више",
   "server_banner.server_stats": "Статистике сервера:",
@@ -558,6 +558,7 @@
   "status.delete": "Избриши",
   "status.detailed_status": "Детаљан приказ разговора",
   "status.direct": "Пошаљи поруку @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Уреди",
   "status.edited": "Уређено {date}",
   "status.edited_x_times": "Уређено {count, plural, one {{count} пут} other {{count} пута}}",
diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json
index fe573949d..90462f026 100644
--- a/app/javascript/mastodon/locales/sv.json
+++ b/app/javascript/mastodon/locales/sv.json
@@ -558,6 +558,7 @@
   "status.delete": "Radera",
   "status.detailed_status": "Detaljerad samtalsvy",
   "status.direct": "Direktmeddela @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Redigera",
   "status.edited": "Ändrad {date}",
   "status.edited_x_times": "Redigerad {count, plural, one {{count} gång} other {{count} gånger}}",
diff --git a/app/javascript/mastodon/locales/szl.json b/app/javascript/mastodon/locales/szl.json
index 587cae5b8..6ec6e8439 100644
--- a/app/javascript/mastodon/locales/szl.json
+++ b/app/javascript/mastodon/locales/szl.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/ta.json b/app/javascript/mastodon/locales/ta.json
index fa2ad1b1c..521b58fe5 100644
--- a/app/javascript/mastodon/locales/ta.json
+++ b/app/javascript/mastodon/locales/ta.json
@@ -558,6 +558,7 @@
   "status.delete": "நீக்கு",
   "status.detailed_status": "விரிவான உரையாடல் காட்சி",
   "status.direct": "நேரடி செய்தி @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/tai.json b/app/javascript/mastodon/locales/tai.json
index 37f8add8f..4c32ffcb5 100644
--- a/app/javascript/mastodon/locales/tai.json
+++ b/app/javascript/mastodon/locales/tai.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/te.json b/app/javascript/mastodon/locales/te.json
index 190db00a0..2a6f612d3 100644
--- a/app/javascript/mastodon/locales/te.json
+++ b/app/javascript/mastodon/locales/te.json
@@ -558,6 +558,7 @@
   "status.delete": "తొలగించు",
   "status.detailed_status": "వివరణాత్మక సంభాషణ వీక్షణ",
   "status.direct": "@{name}కు నేరుగా సందేశం పంపు",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json
index bd4b2fc41..eb7de5aa5 100644
--- a/app/javascript/mastodon/locales/th.json
+++ b/app/javascript/mastodon/locales/th.json
@@ -67,8 +67,8 @@
   "account.unmute_notifications": "เลิกซ่อนการแจ้งเตือนจาก @{name}",
   "account.unmute_short": "เลิกซ่อน",
   "account_note.placeholder": "คลิกเพื่อเพิ่มหมายเหตุ",
-  "admin.dashboard.daily_retention": "อัตราการเก็บรักษาผู้ใช้ตามวันหลังจากลงทะเบียน",
-  "admin.dashboard.monthly_retention": "อัตราการเก็บรักษาผู้ใช้ตามเดือนหลังจากลงทะเบียน",
+  "admin.dashboard.daily_retention": "อัตราการเก็บรักษาผู้ใช้ตามวันหลังจากการลงทะเบียน",
+  "admin.dashboard.monthly_retention": "อัตราการเก็บรักษาผู้ใช้ตามเดือนหลังจากการลงทะเบียน",
   "admin.dashboard.retention.average": "ค่าเฉลี่ย",
   "admin.dashboard.retention.cohort": "เดือนที่ลงทะเบียน",
   "admin.dashboard.retention.cohort_size": "ผู้ใช้ใหม่",
@@ -558,6 +558,7 @@
   "status.delete": "ลบ",
   "status.detailed_status": "มุมมองการสนทนาโดยละเอียด",
   "status.direct": "ส่งข้อความโดยตรงถึง @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "แก้ไข",
   "status.edited": "แก้ไขเมื่อ {date}",
   "status.edited_x_times": "แก้ไข {count, plural, other {{count} ครั้ง}}",
diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json
index fd7546e1d..7e406f5f8 100644
--- a/app/javascript/mastodon/locales/tr.json
+++ b/app/javascript/mastodon/locales/tr.json
@@ -558,6 +558,7 @@
   "status.delete": "Sil",
   "status.detailed_status": "Ayrıntılı sohbet görünümü",
   "status.direct": "@{name} adlı kişiye direkt mesaj",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Düzenle",
   "status.edited": "{date} tarihinde düzenlenmiş",
   "status.edited_x_times": "{count, plural, one {{count} kez} other {{count} kez}} düzenlendi",
diff --git a/app/javascript/mastodon/locales/tt.json b/app/javascript/mastodon/locales/tt.json
index beffae95c..8307a455c 100644
--- a/app/javascript/mastodon/locales/tt.json
+++ b/app/javascript/mastodon/locales/tt.json
@@ -558,6 +558,7 @@
   "status.delete": "Бетерү",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Үзгәртү",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/ug.json b/app/javascript/mastodon/locales/ug.json
index b81bb1356..ee4d3a7f9 100644
--- a/app/javascript/mastodon/locales/ug.json
+++ b/app/javascript/mastodon/locales/ug.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json
index cbdfef527..9cc83b39d 100644
--- a/app/javascript/mastodon/locales/uk.json
+++ b/app/javascript/mastodon/locales/uk.json
@@ -558,6 +558,7 @@
   "status.delete": "Видалити",
   "status.detailed_status": "Детальний вигляд бесіди",
   "status.direct": "Пряме повідомлення до @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Редагувати",
   "status.edited": "Відредаговано {date}",
   "status.edited_x_times": "Відредаговано {count, plural, one {{count} раз} few {{count} рази} many {{counter} разів} other {{counter} разів}}",
diff --git a/app/javascript/mastodon/locales/ur.json b/app/javascript/mastodon/locales/ur.json
index bbe7fbd23..ad21ef963 100644
--- a/app/javascript/mastodon/locales/ur.json
+++ b/app/javascript/mastodon/locales/ur.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/uz.json b/app/javascript/mastodon/locales/uz.json
index 6dcc0e3d8..9c8675208 100644
--- a/app/javascript/mastodon/locales/uz.json
+++ b/app/javascript/mastodon/locales/uz.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json
index a108a6619..ad7518d98 100644
--- a/app/javascript/mastodon/locales/vi.json
+++ b/app/javascript/mastodon/locales/vi.json
@@ -272,7 +272,7 @@
   "footer.get_app": "Ứng dụng",
   "footer.invite": "Mời bạn bè",
   "footer.keyboard_shortcuts": "Phím tắt",
-  "footer.privacy_policy": "Chính sách bảo mật",
+  "footer.privacy_policy": "Bảo mật",
   "footer.source_code": "Mã nguồn",
   "footer.status": "Trạng thái",
   "generic.saved": "Đã lưu",
@@ -558,6 +558,7 @@
   "status.delete": "Xóa",
   "status.detailed_status": "Xem chi tiết thêm",
   "status.direct": "Nhắn riêng @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Sửa",
   "status.edited": "Đã sửa {date}",
   "status.edited_x_times": "Đã sửa {count, plural, other {{count} lần}}",
diff --git a/app/javascript/mastodon/locales/zgh.json b/app/javascript/mastodon/locales/zgh.json
index f5ca66b3a..700a6d6ab 100644
--- a/app/javascript/mastodon/locales/zgh.json
+++ b/app/javascript/mastodon/locales/zgh.json
@@ -558,6 +558,7 @@
   "status.delete": "ⴽⴽⵙ",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "ⵜⵓⵣⵉⵏⵜ ⵜⵓⵙⵔⵉⴷⵜ ⵉ @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index 3d0290e5d..75554c9ea 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -558,6 +558,7 @@
   "status.delete": "删除",
   "status.detailed_status": "详细的对话视图",
   "status.direct": "私信 @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "编辑",
   "status.edited": "编辑于 {date}",
   "status.edited_x_times": "共编辑 {count, plural, one {{count} 次} other {{count} 次}}",
diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json
index 86c14b180..db272a35a 100644
--- a/app/javascript/mastodon/locales/zh-HK.json
+++ b/app/javascript/mastodon/locales/zh-HK.json
@@ -558,6 +558,7 @@
   "status.delete": "刪除",
   "status.detailed_status": "詳細對話內容",
   "status.direct": "私訊 @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "編輯",
   "status.edited": "編輯於 {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} 次} other {{count} 次}}",
diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json
index 6c6871699..1a73ed6f2 100644
--- a/app/javascript/mastodon/locales/zh-TW.json
+++ b/app/javascript/mastodon/locales/zh-TW.json
@@ -558,6 +558,7 @@
   "status.delete": "刪除",
   "status.detailed_status": "詳細的對話內容",
   "status.direct": "發送私訊給 @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "編輯",
   "status.edited": "編輯於 {date}",
   "status.edited_x_times": "已編輯 {count, plural, one {{count} 次} other {{count} 次}}",
diff --git a/app/javascript/mastodon/performance.js b/app/javascript/mastodon/performance.js
index 450a90626..2b7e1bda8 100644
--- a/app/javascript/mastodon/performance.js
+++ b/app/javascript/mastodon/performance.js
@@ -12,6 +12,7 @@ if (process.env.NODE_ENV === 'development') {
     // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1331135
     performance.setResourceTimingBufferSize(Infinity);
   }
+  // eslint-disable-next-line import/no-extraneous-dependencies
   marky = require('marky');
   // allows us to easily do e.g. ReactPerf.printWasted() while debugging
   //window.ReactPerf = require('react-addons-perf');
diff --git a/app/javascript/mastodon/utils/resize_image.js b/app/javascript/mastodon/utils/resize_image.js
deleted file mode 100644
index fb8c3c11e..000000000
--- a/app/javascript/mastodon/utils/resize_image.js
+++ /dev/null
@@ -1,189 +0,0 @@
-import EXIF from 'exif-js';
-
-const MAX_IMAGE_PIXELS = 2073600; // 1920x1080px
-
-const _browser_quirks = {};
-
-// Some browsers will automatically draw images respecting their EXIF orientation
-// while others won't, and the safest way to detect that is to examine how it
-// is done on a known image.
-// See https://github.com/w3c/csswg-drafts/issues/4666
-// and https://github.com/blueimp/JavaScript-Load-Image/commit/1e4df707821a0afcc11ea0720ee403b8759f3881
-const dropOrientationIfNeeded = (orientation) => new Promise(resolve => {
-  switch (_browser_quirks['image-orientation-automatic']) {
-  case true:
-    resolve(1);
-    break;
-  case false:
-    resolve(orientation);
-    break;
-  default:
-    // black 2x1 JPEG, with the following meta information set:
-    // - EXIF Orientation: 6 (Rotated 90° CCW)
-    const testImageURL =
-      'data:image/jpeg;base64,/9j/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAYAAAA' +
-      'AAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA' +
-      'QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE' +
-      'BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAAEAAgMBEQACEQEDEQH/x' +
-      'ABKAAEAAAAAAAAAAAAAAAAAAAALEAEAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAA' +
-      'AAAAAEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/8H//2Q==';
-    const img = new Image();
-    img.onload = () => {
-      const automatic = (img.width === 1 && img.height === 2);
-      _browser_quirks['image-orientation-automatic'] = automatic;
-      resolve(automatic ? 1 : orientation);
-    };
-    img.onerror = () => {
-      _browser_quirks['image-orientation-automatic'] = false;
-      resolve(orientation);
-    };
-    img.src = testImageURL;
-  }
-});
-
-// Some browsers don't allow reading from a canvas and instead return all-white
-// or randomized data. Use a pre-defined image to check if reading the canvas
-// works.
-const checkCanvasReliability = () => new Promise((resolve, reject) => {
-  switch(_browser_quirks['canvas-read-unreliable']) {
-  case true:
-    reject('Canvas reading unreliable');
-    break;
-  case false:
-    resolve();
-    break;
-  default:
-    // 2×2 GIF with white, red, green and blue pixels
-    const testImageURL =
-      'data:image/gif;base64,R0lGODdhAgACAKEDAAAA//8AAAD/AP///ywAAAAAAgACAAACA1wEBQA7';
-    const refData =
-      [255, 255, 255, 255,  255, 0, 0, 255,  0, 255, 0, 255,  0, 0, 255, 255];
-    const img = new Image();
-    img.onload = () => {
-      const canvas  = document.createElement('canvas');
-      const context = canvas.getContext('2d');
-      context.drawImage(img, 0, 0, 2, 2);
-      const imageData = context.getImageData(0, 0, 2, 2);
-      if (imageData.data.every((x, i) => refData[i] === x)) {
-        _browser_quirks['canvas-read-unreliable'] = false;
-        resolve();
-      } else {
-        _browser_quirks['canvas-read-unreliable'] = true;
-        reject('Canvas reading unreliable');
-      }
-    };
-    img.onerror = () => {
-      _browser_quirks['canvas-read-unreliable'] = true;
-      reject('Failed to load test image');
-    };
-    img.src = testImageURL;
-  }
-});
-
-const getImageUrl = inputFile => new Promise((resolve, reject) => {
-  if (window.URL && URL.createObjectURL) {
-    try {
-      resolve(URL.createObjectURL(inputFile));
-    } catch (error) {
-      reject(error);
-    }
-    return;
-  }
-
-  const reader = new FileReader();
-  reader.onerror = (...args) => reject(...args);
-  reader.onload  = ({ target }) => resolve(target.result);
-
-  reader.readAsDataURL(inputFile);
-});
-
-const loadImage = inputFile => new Promise((resolve, reject) => {
-  getImageUrl(inputFile).then(url => {
-    const img = new Image();
-
-    img.onerror = (...args) => reject(...args);
-    img.onload  = () => resolve(img);
-
-    img.src = url;
-  }).catch(reject);
-});
-
-const getOrientation = (img, type = 'image/png') => new Promise(resolve => {
-  if (!['image/jpeg', 'image/webp'].includes(type)) {
-    resolve(1);
-    return;
-  }
-
-  EXIF.getData(img, () => {
-    const orientation = EXIF.getTag(img, 'Orientation');
-    if (orientation !== 1) {
-      dropOrientationIfNeeded(orientation).then(resolve).catch(() => resolve(orientation));
-    } else {
-      resolve(orientation);
-    }
-  });
-});
-
-const processImage = (img, { width, height, orientation, type = 'image/png' }) => new Promise(resolve => {
-  const canvas  = document.createElement('canvas');
-
-  if (4 < orientation && orientation < 9) {
-    canvas.width  = height;
-    canvas.height = width;
-  } else {
-    canvas.width  = width;
-    canvas.height = height;
-  }
-
-  const context = canvas.getContext('2d');
-
-  switch (orientation) {
-  case 2: context.transform(-1, 0, 0, 1, width, 0); break;
-  case 3: context.transform(-1, 0, 0, -1, width, height); break;
-  case 4: context.transform(1, 0, 0, -1, 0, height); break;
-  case 5: context.transform(0, 1, 1, 0, 0, 0); break;
-  case 6: context.transform(0, 1, -1, 0, height, 0); break;
-  case 7: context.transform(0, -1, -1, 0, height, width); break;
-  case 8: context.transform(0, -1, 1, 0, 0, width); break;
-  }
-
-  context.drawImage(img, 0, 0, width, height);
-
-  canvas.toBlob(resolve, type);
-});
-
-const resizeImage = (img, type = 'image/png') => new Promise((resolve, reject) => {
-  const { width, height } = img;
-
-  const newWidth  = Math.round(Math.sqrt(MAX_IMAGE_PIXELS * (width / height)));
-  const newHeight = Math.round(Math.sqrt(MAX_IMAGE_PIXELS * (height / width)));
-
-  checkCanvasReliability()
-    .then(getOrientation(img, type))
-    .then(orientation => processImage(img, {
-      width: newWidth,
-      height: newHeight,
-      orientation,
-      type,
-    }))
-    .then(resolve)
-    .catch(reject);
-});
-
-export default inputFile => new Promise((resolve) => {
-  if (!inputFile.type.match(/image.*/) || inputFile.type === 'image/gif') {
-    resolve(inputFile);
-    return;
-  }
-
-  loadImage(inputFile).then(img => {
-    if (img.width * img.height < MAX_IMAGE_PIXELS) {
-      resolve(inputFile);
-      return;
-    }
-
-    resizeImage(img, inputFile.type)
-      .then(resolve)
-      .catch(() => resolve(inputFile));
-  }).catch(() => resolve(inputFile));
-});
diff --git a/app/javascript/styles/mastodon-light/diff.scss b/app/javascript/styles/mastodon-light/diff.scss
index 58f161f81..7498477ca 100644
--- a/app/javascript/styles/mastodon-light/diff.scss
+++ b/app/javascript/styles/mastodon-light/diff.scss
@@ -264,7 +264,7 @@ html {
 
 // Change the background colors of statuses
 .focusable:focus {
-  background: $ui-base-color;
+  background: lighten($white, 4%);
 }
 
 .detailed-status,
@@ -697,3 +697,11 @@ html {
     url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(lighten($ui-base-color, 8%))}'/></svg>")
     no-repeat right 8px center / auto 16px;
 }
+
+.status__wrapper-direct {
+  background-color: rgba($ui-highlight-color, 0.1);
+
+  &:focus {
+    background-color: rgba($ui-highlight-color, 0.15);
+  }
+}
diff --git a/app/javascript/styles/mastodon/about.scss b/app/javascript/styles/mastodon/about.scss
index 0183c43d5..0f02563b4 100644
--- a/app/javascript/styles/mastodon/about.scss
+++ b/app/javascript/styles/mastodon/about.scss
@@ -28,14 +28,14 @@ $fluid-breakpoint: $maximum-width + 20px;
     position: relative;
     border-bottom: 1px solid lighten($ui-base-color, 8%);
     padding: 1em 1.75em;
-    padding-left: 3em;
+    padding-inline-start: 3em;
     font-weight: 500;
     counter-increment: list-counter;
 
     &::before {
       content: counter(list-counter);
       position: absolute;
-      left: 0;
+      inset-inline-start: 0;
       top: 50%;
       transform: translateY(-50%);
       background: $highlight-text-color;
diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss
index c007eb4b5..af0d8b5ed 100644
--- a/app/javascript/styles/mastodon/accounts.scss
+++ b/app/javascript/styles/mastodon/accounts.scss
@@ -73,8 +73,8 @@
     }
 
     .display-name {
-      margin-left: 15px;
-      text-align: left;
+      margin-inline-start: 15px;
+      text-align: start;
 
       i[data-hidden] {
         display: none;
@@ -138,22 +138,22 @@
   }
 
   .older {
-    float: left;
-    padding-left: 0;
+    float: inline-start;
+    padding-inline-start: 0;
 
     .fa {
       display: inline-block;
-      margin-right: 5px;
+      margin-inline-end: 5px;
     }
   }
 
   .newer {
-    float: right;
-    padding-right: 0;
+    float: inline-end;
+    padding-inline-end: 0;
 
     .fa {
       display: inline-block;
-      margin-left: 5px;
+      margin-inline-start: 5px;
     }
   }
 
diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss
index 240c90735..1c74de256 100644
--- a/app/javascript/styles/mastodon/admin.scss
+++ b/app/javascript/styles/mastodon/admin.scss
@@ -117,7 +117,7 @@ $content-width: 840px;
         text-overflow: ellipsis;
 
         i.fa {
-          margin-right: 5px;
+          margin-inline-end: 5px;
         }
 
         &:hover {
@@ -186,7 +186,10 @@ $content-width: 840px;
   }
 
   .content {
-    padding: 55px 15px 20px 25px;
+    padding-top: 55px;
+    padding-bottom: 20px;
+    padding-inline-start: 25px;
+    padding-inline-end: 15px;
 
     @media screen and (max-width: $no-columns-breakpoint) {
       max-width: none;
@@ -202,11 +205,12 @@ $content-width: 840px;
         flex-wrap: wrap;
         align-items: center;
         justify-content: space-between;
-        margin: -15px -15px 0 0;
+        margin-top: -15px;
+        margin-inline-end: -15px;
 
         & > * {
           margin-top: 15px;
-          margin-right: 15px;
+          margin-inline-end: 15px;
         }
       }
 
@@ -385,7 +389,7 @@ $content-width: 840px;
           z-index: 10;
           width: 100%;
           height: calc(100% - 56px);
-          left: 0;
+          inset-inline-start: 0;
           bottom: 0;
           overflow-y: auto;
           background: $ui-base-color;
@@ -470,10 +474,11 @@ body,
 .filters {
   display: flex;
   flex-wrap: wrap;
+  gap: 40px;
 
   .filter-subset {
     flex: 0 0 auto;
-    margin: 0 40px 20px 0;
+    margin-bottom: 20px;
 
     &:last-child {
       margin-bottom: 30px;
@@ -485,7 +490,7 @@ body,
 
       li {
         display: inline-block;
-        margin-right: 5px;
+        margin-inline-end: 5px;
       }
     }
 
@@ -588,7 +593,7 @@ body,
 
   .activity-stream {
     flex: 2 0 0;
-    margin-right: 20px;
+    margin-inline-end: 20px;
     max-width: calc(100% - 60px);
 
     .entry {
@@ -641,12 +646,12 @@ body,
   }
 
   .media-spoiler-toggle-buttons {
-    margin-left: auto;
+    margin-inline-start: auto;
 
     .button {
       overflow: visible;
       margin: 0 0 5px 5px;
-      float: right;
+      float: inline-end;
     }
   }
 }
@@ -667,7 +672,7 @@ body,
 
 .special-action-button,
 .back-link {
-  text-align: right;
+  text-align: end;
   flex: 1 1 auto;
 }
 
@@ -685,7 +690,7 @@ body,
   display: block;
   line-height: 20px;
   padding: 15px;
-  padding-left: 15px * 2 + 40px;
+  padding-inline-start: 15px * 2 + 40px;
   background: $ui-base-color;
   border-bottom: 1px solid darken($ui-base-color, 8%);
   position: relative;
@@ -712,7 +717,7 @@ body,
 
   &__avatar {
     position: absolute;
-    left: 15px;
+    inset-inline-start: 15px;
     top: 15px;
 
     .avatar {
@@ -780,7 +785,7 @@ a.name-tag,
   .avatar {
     display: block;
     margin: 0;
-    margin-right: 5px;
+    margin-inline-end: 5px;
     border-radius: 50%;
   }
 
@@ -794,7 +799,7 @@ a.name-tag,
 
 .speech-bubble {
   margin-bottom: 20px;
-  border-left: 4px solid $ui-highlight-color;
+  border-inset-inline-start: 4px solid $ui-highlight-color;
 
   &.positive {
     border-left-color: $success-green;
@@ -810,7 +815,7 @@ a.name-tag,
 
   &__bubble {
     padding: 16px;
-    padding-left: 14px;
+    padding-inline-start: 14px;
     font-size: 15px;
     line-height: 20px;
     border-radius: 4px 4px 4px 0;
@@ -824,7 +829,7 @@ a.name-tag,
 
   &__owner {
     padding: 8px;
-    padding-left: 12px;
+    padding-inline-start: 12px;
   }
 
   time {
@@ -848,7 +853,7 @@ a.name-tag,
       border: 0;
 
       &__avatar-wrapper {
-        margin-left: 0;
+        margin-inline-start: 0;
       }
     }
 
@@ -857,7 +862,7 @@ a.name-tag,
       font-weight: 500;
       color: $darker-text-color;
       text-transform: uppercase;
-      text-align: right;
+      text-align: end;
 
       a {
         color: inherit;
@@ -908,7 +913,7 @@ a.name-tag,
 
         &__icon {
           color: $dark-text-color;
-          margin-right: 4px;
+          margin-inline-end: 4px;
           font-weight: 500;
         }
       }
@@ -1106,7 +1111,7 @@ a.name-tag,
 
   > h4 {
     position: sticky;
-    left: 0;
+    inset-inline-start: 0;
   }
 
   &__table {
@@ -1118,7 +1123,7 @@ a.name-tag,
     &__date {
       white-space: nowrap;
       padding: 10px 0;
-      text-align: left;
+      text-align: start;
       min-width: 120px;
 
       &.retention__table__average {
@@ -1176,7 +1181,7 @@ a.name-tag,
 
     &__total {
       display: block;
-      margin-right: 10px;
+      margin-inline-end: 10px;
       font-weight: 500;
       font-size: 28px;
       color: $primary-text-color;
@@ -1278,7 +1283,7 @@ a.sparkline {
     }
 
     &__value {
-      text-align: right;
+      text-align: end;
       color: $darker-text-color;
       padding: 11px 10px;
     }
@@ -1289,7 +1294,7 @@ a.sparkline {
       height: 8px;
       border-radius: 50%;
       background: $ui-highlight-color;
-      margin-right: 10px;
+      margin-inline-end: 10px;
 
       @for $i from 0 through 10 {
         &--#{10 * $i} {
@@ -1325,7 +1330,7 @@ a.sparkline {
     }
 
     &__rules {
-      margin-left: 30px;
+      margin-inline-start: 30px;
     }
   }
 
@@ -1447,7 +1452,7 @@ a.sparkline {
       height: 21px;
       position: absolute;
       bottom: 0;
-      right: 15px;
+      inset-inline-end: 15px;
       background: linear-gradient(to left, $ui-base-color, transparent);
       pointer-events: none;
     }
@@ -1527,7 +1532,7 @@ a.sparkline {
     background: $ui-base-color;
     position: relative;
     padding: 15px;
-    padding-left: 15px * 2 + 40px;
+    padding-inline-start: 15px * 2 + 40px;
     border-bottom: 1px solid darken($ui-base-color, 8%);
 
     &:first-child {
@@ -1547,7 +1552,7 @@ a.sparkline {
 
     &__avatar {
       position: absolute;
-      left: 15px;
+      inset-inline-start: 15px;
       top: 15px;
       border-radius: 4px;
       width: 40px;
@@ -1563,7 +1568,7 @@ a.sparkline {
       .username {
         color: $primary-text-color;
         font-weight: 500;
-        margin-right: 5px;
+        margin-inline-end: 5px;
 
         a {
           color: inherit;
@@ -1578,7 +1583,7 @@ a.sparkline {
       }
 
       time {
-        margin-left: 5px;
+        margin-inline-start: 5px;
         vertical-align: baseline;
       }
     }
@@ -1613,8 +1618,8 @@ a.sparkline {
     &__actions {
       position: absolute;
       top: 15px;
-      right: 15px;
-      text-align: right;
+      inset-inline-end: 15px;
+      text-align: end;
     }
   }
 }
@@ -1637,7 +1642,7 @@ a.sparkline {
       flex: 0 0 auto;
       width: 200px;
       padding: 15px;
-      padding-right: 0;
+      padding-inline-end: 0;
 
       .button {
         display: block;
@@ -1673,7 +1678,7 @@ a.sparkline {
 }
 
 .section-skip-link {
-  float: right;
+  float: inline-end;
 
   a {
     color: $ui-highlight-color;
@@ -1723,7 +1728,7 @@ a.sparkline {
 
   &__rules {
     list-style: disc;
-    padding-left: 15px;
+    padding-inline-start: 15px;
     margin-bottom: 20px;
     color: $darker-text-color;
 
@@ -1812,7 +1817,7 @@ a.sparkline {
 
   li {
     counter-increment: step 1;
-    padding-left: 2.5rem;
+    padding-inline-start: 2.5rem;
     padding-bottom: 8px;
     position: relative;
     margin-bottom: 8px;
@@ -1822,7 +1827,7 @@ a.sparkline {
       content: counter(step);
       font-size: 0.625rem;
       font-weight: 500;
-      left: 0;
+      inset-inline-start: 0;
       display: flex;
       justify-content: center;
       align-items: center;
@@ -1841,7 +1846,7 @@ a.sparkline {
       background: $highlight-text-color;
       bottom: 0;
       top: calc(1.875rem + 1px);
-      left: 0.6875rem;
+      inset-inline-start: 0.6875rem;
     }
 
     &:last-child {
diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss
index 1d08b12e5..a344c7fa4 100644
--- a/app/javascript/styles/mastodon/basics.scss
+++ b/app/javascript/styles/mastodon/basics.scss
@@ -267,7 +267,7 @@ button {
   overflow: hidden;
   position: absolute;
   top: 0;
-  left: 0;
+  inset-inline-start: 0;
   z-index: -1000;
 }
 
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 4f4f447b9..3ff786e62 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -309,7 +309,7 @@
   &__counter {
     display: inline-block;
     width: auto;
-    margin-left: 4px;
+    margin-inline-start: 4px;
     font-size: 12px;
     font-weight: 500;
   }
@@ -413,7 +413,7 @@ body > [data-popper-placement] {
       width: 18px;
       height: 18px;
       flex: 0 0 auto;
-      margin-right: 10px;
+      margin-inline-end: 10px;
       top: -1px;
       border-radius: 4px;
       vertical-align: middle;
@@ -465,7 +465,7 @@ body > [data-popper-placement] {
   .emoji-picker-dropdown {
     position: absolute;
     top: 0;
-    right: 0;
+    inset-inline-end: 0;
   }
 
   .compose-form__autosuggest-wrapper {
@@ -527,7 +527,7 @@ body > [data-popper-placement] {
     min-height: 100px;
     border-radius: 4px 4px 0 0;
     padding-bottom: 0;
-    padding-right: 10px + 22px;
+    padding-right: 10px + 22px; // Cannot use inline-end because of dir=auto
     resize: none;
     scrollbar-color: initial;
 
@@ -536,7 +536,7 @@ body > [data-popper-placement] {
     }
 
     @media screen and (max-width: 600px) {
-      height: 100px !important; // prevent auto-resize textarea
+      height: 100px !important; // Prevent auto-resize textarea
       resize: vertical;
     }
   }
@@ -605,7 +605,7 @@ body > [data-popper-placement] {
 
     &__uses {
       flex: 0 0 auto;
-      text-align: right;
+      text-align: end;
       overflow: hidden;
       text-overflow: ellipsis;
       white-space: nowrap;
@@ -615,7 +615,7 @@ body > [data-popper-placement] {
   .autosuggest-account-icon,
   .autosuggest-emoji img {
     display: block;
-    margin-right: 8px;
+    margin-inline-end: 8px;
     width: 16px;
     height: 16px;
   }
@@ -677,8 +677,8 @@ body > [data-popper-placement] {
         position: absolute;
         z-index: 2;
         bottom: 0;
-        left: 0;
-        right: 0;
+        inset-inline-start: 0;
+        inset-inline-end: 0;
         box-sizing: border-box;
         background: linear-gradient(
           0deg,
@@ -737,7 +737,7 @@ body > [data-popper-placement] {
 
     .character-counter__wrapper {
       align-self: center;
-      margin-right: 4px;
+      margin-inline-end: 4px;
     }
   }
 
@@ -826,7 +826,7 @@ body > [data-popper-placement] {
 }
 
 .reply-indicator__cancel {
-  float: right;
+  float: inline-end;
   line-height: 24px;
 }
 
@@ -836,13 +836,13 @@ body > [data-popper-placement] {
   max-width: 100%;
   line-height: 24px;
   overflow: hidden;
-  padding-right: 25px;
+  padding-inline-end: 25px;
   text-decoration: none;
 }
 
 .reply-indicator__display-avatar {
-  float: left;
-  margin-right: 5px;
+  float: inline-start;
+  margin-inline-end: 5px;
 }
 
 .status__content--with-action {
@@ -1159,7 +1159,7 @@ body > [data-popper-placement] {
 
 .notification__relative_time {
   color: $dark-text-color;
-  float: right;
+  float: inline-end;
   font-size: 14px;
   padding-bottom: 1px;
 }
@@ -1244,7 +1244,7 @@ body > [data-popper-placement] {
 .status__prepend {
   padding: 16px;
   padding-bottom: 0;
-  display: flex;
+  display: inline-flex;
   gap: 10px;
   font-size: 15px;
   line-height: 22px;
@@ -1262,6 +1262,18 @@ body > [data-popper-placement] {
   }
 }
 
+.status__wrapper-direct {
+  background: mix($ui-base-color, $ui-highlight-color, 95%);
+
+  &:focus {
+    background: mix(lighten($ui-base-color, 4%), $ui-highlight-color, 95%);
+  }
+
+  .status__prepend {
+    color: $highlight-text-color;
+  }
+}
+
 .status__action-bar {
   display: flex;
   justify-content: space-between;
@@ -1315,6 +1327,11 @@ body > [data-popper-placement] {
   .audio-player {
     margin-top: 16px;
   }
+
+  .status__prepend {
+    padding: 0;
+    margin-bottom: 16px;
+  }
 }
 
 .detailed-status__meta {
@@ -1333,6 +1350,32 @@ body > [data-popper-placement] {
   padding: 10px 0;
 }
 
+.detailed-status__wrapper-direct {
+  .detailed-status,
+  .detailed-status__action-bar {
+    background: mix($ui-base-color, $ui-highlight-color, 95%);
+  }
+
+  &:focus {
+    .detailed-status,
+    .detailed-status__action-bar {
+      background: mix(lighten($ui-base-color, 4%), $ui-highlight-color, 95%);
+    }
+  }
+
+  .detailed-status__action-bar {
+    border-top-color: mix(
+      lighten($ui-base-color, 8%),
+      $ui-highlight-color,
+      95%
+    );
+  }
+
+  .status__prepend {
+    color: $highlight-text-color;
+  }
+}
+
 .detailed-status__link {
   color: inherit;
   text-decoration: none;
@@ -1344,7 +1387,7 @@ body > [data-popper-placement] {
   font-weight: 500;
   font-size: 12px;
   line-height: 17px;
-  margin-left: 6px;
+  margin-inline-start: 6px;
 }
 
 .reply-indicator__content {
@@ -1389,7 +1432,7 @@ body > [data-popper-placement] {
     border-bottom: 0;
 
     .account__avatar-wrapper {
-      margin-left: 0;
+      margin-inline-start: 0;
     }
   }
 
@@ -1403,10 +1446,12 @@ body > [data-popper-placement] {
     text-decoration: none;
     font-size: 14px;
 
-    &--with-note {
-      strong {
-        display: inline;
-      }
+    .display-name {
+      margin-bottom: 4px;
+    }
+
+    .display-name strong {
+      display: inline;
     }
   }
 
@@ -1450,7 +1495,7 @@ body > [data-popper-placement] {
   &-inline {
     display: inline-block;
     vertical-align: middle;
-    margin-right: 5px;
+    margin-inline-end: 5px;
   }
 
   &-composite {
@@ -1459,7 +1504,7 @@ body > [data-popper-placement] {
     position: relative;
 
     & > div {
-      float: left;
+      float: inline-start;
       position: relative;
       box-sizing: border-box;
     }
@@ -1473,7 +1518,7 @@ body > [data-popper-placement] {
       display: block;
       position: absolute;
       top: 50%;
-      left: 50%;
+      inset-inline-start: 50%;
       transform: translate(-50%, -50%);
       color: $primary-text-color;
       text-shadow: 1px 1px 2px $base-shadow-color;
@@ -1493,7 +1538,7 @@ a .account__avatar {
   &-overlay {
     position: absolute;
     bottom: 0;
-    right: 0;
+    inset-inline-end: 0;
     z-index: 1;
   }
 }
@@ -1550,15 +1595,15 @@ a .account__avatar {
 
   .dropdown--active {
     .dropdown__content.dropdown__right {
-      left: 6px;
-      right: initial;
+      inset-inline-start: 6px;
+      inset-inline-end: initial;
     }
 
     &::after {
       bottom: initial;
-      margin-left: 11px;
+      margin-inline-start: 11px;
       margin-top: -7px;
-      right: initial;
+      inset-inline-end: initial;
     }
   }
 }
@@ -1574,7 +1619,7 @@ a .account__avatar {
   text-decoration: none;
   overflow: hidden;
   flex: 0 1 100%;
-  border-right: 1px solid lighten($ui-base-color, 8%);
+  border-inset-inline-end: 1px solid lighten($ui-base-color, 8%);
   padding: 10px 0;
   border-bottom: 4px solid transparent;
 
@@ -1614,8 +1659,8 @@ a .account__avatar {
 }
 
 .account-authorize__avatar {
-  float: left;
-  margin-right: 10px;
+  float: inline-start;
+  margin-inline-end: 10px;
 }
 
 .status__display-name,
@@ -1629,7 +1674,7 @@ a .account__avatar {
 
 .status__display-name,
 .account__display-name {
-  strong {
+  .display-name strong {
     color: $primary-text-color;
   }
 }
@@ -1644,12 +1689,12 @@ a .account__avatar {
 .reply-indicator__display-name,
 .detailed-status__display-name,
 a.account__display-name {
-  &:hover strong {
+  &:hover .display-name strong {
     text-decoration: underline;
   }
 }
 
-.account__display-name strong {
+.account__display-name .display-name strong {
   display: block;
   overflow: hidden;
   text-overflow: ellipsis;
@@ -2087,7 +2132,7 @@ a.account__display-name {
   }
 
   &.right {
-    left: -9px;
+    inset-inline-start: -9px;
 
     &::before {
       transform: rotate(-90deg);
@@ -2099,7 +2144,7 @@ a.account__display-name {
   }
 
   &.left {
-    right: -9px;
+    inset-inline-end: -9px;
 
     &::before {
       transform: rotate(90deg);
@@ -2169,7 +2214,7 @@ a.account__display-name {
   vertical-align: top;
 
   .account__avatar {
-    margin-right: 5px;
+    margin-inline-end: 5px;
     border-radius: 50%;
   }
 
@@ -2182,8 +2227,8 @@ a.account__display-name {
   display: block;
   line-height: 18px;
   max-width: 311px;
-  right: 0;
-  text-align: left;
+  inset-inline-end: 0;
+  text-align: start;
   z-index: 9999;
 
   & > ul {
@@ -2197,12 +2242,12 @@ a.account__display-name {
   }
 
   &.dropdown__right {
-    right: 0;
+    inset-inline-end: 0;
   }
 
   &.dropdown__left {
     & > ul {
-      left: -98px;
+      inset-inline-start: -98px;
     }
   }
 
@@ -2421,23 +2466,23 @@ $ui-header-height: 55px;
   .drawer {
     flex: 0 0 auto;
     padding: 10px;
-    padding-left: 5px;
-    padding-right: 5px;
+    padding-inline-start: 5px;
+    padding-inline-end: 5px;
 
     &:first-child {
-      padding-left: 10px;
+      padding-inline-start: 10px;
     }
 
     &:last-child {
-      padding-right: 10px;
+      padding-inline-end: 10px;
     }
   }
 
   .columns-area > div {
     .column,
     .drawer {
-      padding-left: 5px;
-      padding-right: 5px;
+      padding-inline-start: 5px;
+      padding-inline-end: 5px;
     }
   }
 }
@@ -2484,7 +2529,7 @@ $ui-header-height: 55px;
   }
 
   span {
-    margin-left: 5px;
+    margin-inline-start: 5px;
     display: none;
   }
 }
@@ -2526,7 +2571,7 @@ $ui-header-height: 55px;
     line-height: 18px;
     font-size: 16px;
     padding: 15px;
-    padding-right: 30px;
+    padding-inline-end: 30px;
   }
 
   .search__icon .fa {
@@ -2594,7 +2639,7 @@ $ui-header-height: 55px;
     .navigation-panel {
       margin: 0;
       background: $ui-base-color;
-      border-left: 1px solid lighten($ui-base-color, 8%);
+      border-inset-inline-start: 1px solid lighten($ui-base-color, 8%);
       height: 100vh;
     }
 
@@ -2666,7 +2711,7 @@ $ui-header-height: 55px;
 
   &__badge {
     position: absolute;
-    left: 9px;
+    inset-inline-start: 9px;
     top: -13px;
     background: $ui-highlight-color;
     border: 2px solid lighten($ui-base-color, 8%);
@@ -2680,7 +2725,7 @@ $ui-header-height: 55px;
 
   &__issue-badge {
     position: absolute;
-    left: 11px;
+    inset-inline-start: 11px;
     bottom: 1px;
     display: block;
     background: $error-red;
@@ -2736,7 +2781,7 @@ $ui-header-height: 55px;
 
   &__background {
     position: absolute;
-    left: 0;
+    inset-inline-start: 0;
     bottom: 0;
     height: 220px;
     width: auto;
@@ -2862,7 +2907,7 @@ $ui-header-height: 55px;
 .drawer__inner {
   position: absolute;
   top: 0;
-  left: 0;
+  inset-inline-start: 0;
   background: lighten($ui-base-color, 13%);
   box-sizing: border-box;
   padding: 0;
@@ -2905,7 +2950,7 @@ $ui-header-height: 55px;
 .pseudo-drawer {
   background: lighten($ui-base-color, 13%);
   font-size: 13px;
-  text-align: left;
+  text-align: start;
 }
 
 .drawer__header {
@@ -3008,7 +3053,7 @@ $ui-header-height: 55px;
 
 .column-back-button__icon {
   display: inline-block;
-  margin-right: 5px;
+  margin-inline-end: 5px;
 }
 
 .column-back-button--slim {
@@ -3021,7 +3066,7 @@ $ui-header-height: 55px;
   font-size: 16px;
   padding: 15px;
   position: absolute;
-  right: 0;
+  inset-inline-end: 0;
   top: -48px;
 }
 
@@ -3086,7 +3131,7 @@ $ui-header-height: 55px;
   margin-top: auto;
   margin-bottom: auto;
   line-height: 0;
-  left: 8px;
+  inset-inline-start: 8px;
   opacity: 0;
   transition: opacity 0.25s ease;
 }
@@ -3105,7 +3150,7 @@ $ui-header-height: 55px;
   margin-top: auto;
   margin-bottom: auto;
   line-height: 0;
-  right: 10px;
+  inset-inline-end: 10px;
   opacity: 1;
   transition: opacity 0.25s ease;
 }
@@ -3117,7 +3162,7 @@ $ui-header-height: 55px;
 .react-toggle-thumb {
   position: absolute;
   top: 1px;
-  left: 1px;
+  inset-inline-start: 1px;
   width: 22px;
   height: 22px;
   border: 1px solid $ui-base-color;
@@ -3129,7 +3174,7 @@ $ui-header-height: 55px;
 }
 
 .react-toggle--checked .react-toggle-thumb {
-  left: 27px;
+  inset-inline-start: 27px;
   border-color: $ui-highlight-color;
 }
 
@@ -3183,7 +3228,7 @@ $ui-header-height: 55px;
 
 .column-link__icon {
   display: inline-block;
-  margin-right: 5px;
+  margin-inline-end: 5px;
 }
 
 .column-link__badge {
@@ -3277,7 +3322,7 @@ $ui-header-height: 55px;
 
   thead {
     position: absolute;
-    left: -9999px;
+    inset-inline-start: -9999px;
   }
 
   td {
@@ -3381,9 +3426,9 @@ button.icon-button.active i.fa-retweet {
 
   &__actions {
     bottom: 0;
-    left: 0;
+    inset-inline-start: 0;
     position: absolute;
-    right: 0;
+    inset-inline-end: 0;
     top: 0;
     display: flex;
     justify-content: center;
@@ -3489,7 +3534,7 @@ a.status-card {
     position: absolute;
     transform-origin: 50% 50%;
     top: 50%;
-    left: 50%;
+    inset-inline-start: 50%;
     transform: translate(-50%, -50%);
   }
 }
@@ -3556,7 +3601,7 @@ a.status-card.compact:hover {
   object-fit: fill;
   position: absolute;
   top: 0;
-  left: 0;
+  inset-inline-start: 0;
   z-index: 0;
   background: $base-overlay-background;
 
@@ -3671,8 +3716,8 @@ a.status-card.compact:hover {
       content: '';
       position: absolute;
       bottom: -13px;
-      left: 0;
-      right: 0;
+      inset-inline-start: 0;
+      inset-inline-end: 0;
       margin: 0 auto;
       width: 60%;
       pointer-events: none;
@@ -3707,11 +3752,12 @@ a.status-card.compact:hover {
   & > button {
     margin: 0;
     border: 0;
-    padding: 15px 0 15px 15px;
+    padding: 15px;
+    padding-inline-end: 0;
     color: inherit;
     background: transparent;
     font: inherit;
-    text-align: left;
+    text-align: start;
     text-overflow: ellipsis;
     overflow: hidden;
     white-space: nowrap;
@@ -3745,7 +3791,7 @@ a.status-card.compact:hover {
 }
 
 .column-header__links .text-btn {
-  margin-right: 10px;
+  margin-inline-end: 10px;
 }
 
 .column-header__button {
@@ -3828,18 +3874,18 @@ a.status-card.compact:hover {
 }
 
 .column-header__setting-arrows {
-  float: right;
+  float: inline-end;
 
   .column-header__setting-btn {
     padding: 5px;
 
     &:first-child {
-      padding-right: 7px;
+      padding-inline-end: 7px;
     }
 
     &:last-child {
-      padding-left: 7px;
-      margin-left: 5px;
+      padding-inline-start: 7px;
+      margin-inline-start: 5px;
     }
   }
 }
@@ -3866,7 +3912,7 @@ a.status-card.compact:hover {
 
 .column-header__icon {
   display: inline-block;
-  margin-right: 5px;
+  margin-inline-end: 5px;
 }
 
 .loading-indicator {
@@ -3877,7 +3923,7 @@ a.status-card.compact:hover {
   overflow: visible;
   position: absolute;
   top: 50%;
-  left: 50%;
+  inset-inline-start: 50%;
   transform: translate(-50%, -50%);
   display: flex;
   align-items: center;
@@ -4013,7 +4059,7 @@ a.status-card.compact:hover {
 
 .spoiler-button {
   top: 0;
-  left: 0;
+  inset-inline-start: 0;
   width: 100%;
   height: 100%;
   position: absolute;
@@ -4021,7 +4067,7 @@ a.status-card.compact:hover {
 
   &--minified {
     display: block;
-    left: 4px;
+    inset-inline-start: 4px;
     top: 4px;
     width: auto;
     height: auto;
@@ -4138,12 +4184,12 @@ a.status-card.compact:hover {
 
     &__placeholder {
       color: $dark-text-color;
-      padding-left: 2px;
+      padding-inline-start: 2px;
       font-size: 12px;
     }
 
     &__value-container {
-      padding-left: 6px;
+      padding-inline-start: 6px;
     }
 
     &__multi-value {
@@ -4240,7 +4286,7 @@ a.status-card.compact:hover {
   color: $darker-text-color;
   display: inline-block;
   margin-bottom: 14px;
-  margin-left: 8px;
+  margin-inline-start: 8px;
   vertical-align: middle;
 }
 
@@ -4425,7 +4471,7 @@ a.status-card.compact:hover {
 .emoji-picker-dropdown__modifiers {
   position: absolute;
   top: 60px;
-  right: 11px;
+  inset-inline-end: 11px;
   cursor: pointer;
 }
 
@@ -4433,7 +4479,7 @@ a.status-card.compact:hover {
   position: absolute;
   z-index: 4;
   top: -4px;
-  left: -8px;
+  inset-inline-start: -8px;
   background: $simple-background-color;
   border-radius: 4px;
   box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
@@ -4470,7 +4516,7 @@ a.status-card.compact:hover {
   display: flex;
   height: 100vh;
   justify-content: center;
-  left: 0;
+  inset-inline-start: 0;
   opacity: 0;
   position: fixed;
   top: 0;
@@ -4495,9 +4541,9 @@ a.status-card.compact:hover {
 .upload-area__background {
   position: absolute;
   top: 0;
-  right: 0;
+  inset-inline-end: 0;
   bottom: 0;
-  left: 0;
+  inset-inline-start: 0;
   z-index: -1;
   border-radius: 4px;
   background: $ui-base-color;
@@ -4525,7 +4571,7 @@ a.status-card.compact:hover {
 
   .fa {
     font-size: 34px;
-    margin-right: 10px;
+    margin-inline-end: 10px;
   }
 
   span {
@@ -4551,7 +4597,7 @@ a.status-card.compact:hover {
 
 .upload-progress__tracker {
   position: absolute;
-  left: 0;
+  inset-inline-start: 0;
   top: 0;
   height: 6px;
   background: $ui-highlight-color;
@@ -4560,7 +4606,10 @@ a.status-card.compact:hover {
 
 .emoji-button {
   display: block;
-  padding: 5px 5px 2px 2px;
+  padding-top: 5px;
+  padding-bottom: 2px;
+  padding-inline-start: 2px;
+  padding-inline-end: 5px;
   outline: 0;
   cursor: pointer;
 
@@ -4648,7 +4697,7 @@ a.status-card.compact:hover {
   display: flex;
   align-items: center;
   justify-content: center;
-  margin-right: 10px;
+  margin-inline-end: 10px;
 }
 
 .privacy-dropdown__option__content {
@@ -4714,11 +4763,11 @@ a.status-card.compact:hover {
     }
 
     .emoji-mart-search {
-      padding-right: 10px;
+      padding-inline-end: 10px;
     }
 
     .emoji-mart-search-icon {
-      right: 10px + 5px;
+      inset-inline-end: 10px + 5px;
     }
 
     .emoji-mart-scroll {
@@ -4771,7 +4820,7 @@ a.status-card.compact:hover {
 
   display: block;
   padding: 15px;
-  padding-right: 30px;
+  padding-inline-end: 30px;
   line-height: 18px;
   font-size: 16px;
 
@@ -4807,7 +4856,7 @@ a.status-card.compact:hover {
   .fa {
     position: absolute;
     top: 16px;
-    right: 10px;
+    inset-inline-end: 10px;
     z-index: 2;
     display: inline-block;
     opacity: 0;
@@ -4861,7 +4910,7 @@ a.status-card.compact:hover {
 
   .fa {
     display: inline-block;
-    margin-right: 5px;
+    margin-inline-end: 5px;
   }
 }
 
@@ -4880,7 +4929,7 @@ a.status-card.compact:hover {
 
     .fa {
       display: inline-block;
-      margin-right: 5px;
+      margin-inline-end: 5px;
     }
   }
 
@@ -4918,8 +4967,8 @@ a.status-card.compact:hover {
 .modal-root__overlay {
   position: fixed;
   top: 0;
-  left: 0;
-  right: 0;
+  inset-inline-start: 0;
+  inset-inline-end: 0;
   bottom: 0;
   background: rgba($base-overlay-background, 0.7);
   transition: background 0.5s;
@@ -4928,7 +4977,7 @@ a.status-card.compact:hover {
 .modal-root__container {
   position: fixed;
   top: 0;
-  left: 0;
+  inset-inline-start: 0;
   width: 100%;
   height: 100%;
   box-sizing: border-box;
@@ -4981,16 +5030,16 @@ a.status-card.compact:hover {
 .media-modal__closer {
   position: absolute;
   top: 0;
-  left: 0;
-  right: 0;
+  inset-inline-start: 0;
+  inset-inline-end: 0;
   bottom: 0;
 }
 
 .media-modal__navigation {
   position: absolute;
   top: 0;
-  left: 0;
-  right: 0;
+  inset-inline-start: 0;
+  inset-inline-end: 0;
   bottom: 0;
   pointer-events: none;
   transition: opacity 0.3s linear;
@@ -5033,18 +5082,18 @@ a.status-card.compact:hover {
 }
 
 .media-modal__nav--left {
-  left: 0;
+  inset-inline-start: 0;
 }
 
 .media-modal__nav--right {
-  right: 0;
+  inset-inline-end: 0;
 }
 
 .media-modal__overlay {
   max-width: 600px;
   position: absolute;
-  left: 0;
-  right: 0;
+  inset-inline-start: 0;
+  inset-inline-end: 0;
   bottom: 0;
   margin: 0 auto;
 
@@ -5131,14 +5180,14 @@ a.status-card.compact:hover {
 
 .media-modal__close {
   position: absolute;
-  right: 8px;
+  inset-inline-end: 8px;
   top: 8px;
   z-index: 100;
 }
 
 .media-modal__zoom-button {
   position: absolute;
-  right: 64px;
+  inset-inline-end: 64px;
   top: 8px;
   z-index: 100;
   pointer-events: auto;
@@ -5172,7 +5221,7 @@ a.status-card.compact:hover {
   & > div {
     position: absolute;
     top: 0;
-    left: 0;
+    inset-inline-start: 0;
     width: 100%;
     height: 100%;
     box-sizing: border-box;
@@ -5268,7 +5317,7 @@ a.status-card.compact:hover {
   display: inline-block;
   max-width: 30px;
   max-height: auto;
-  margin-left: 10px;
+  margin-inline-start: 10px;
 }
 
 .boost-modal,
@@ -5323,9 +5372,9 @@ a.status-card.compact:hover {
 
   & > div {
     flex: 1 1 auto;
-    text-align: right;
+    text-align: end;
     color: $lighter-text-color;
-    padding-right: 10px;
+    padding-inline-end: 10px;
   }
 
   .button {
@@ -5528,7 +5577,7 @@ a.status-card.compact:hover {
     & > span {
       font-size: 17px;
       font-weight: 500;
-      margin-left: 10px;
+      margin-inline-start: 10px;
     }
   }
 
@@ -5552,11 +5601,11 @@ a.status-card.compact:hover {
   }
 
   .emoji-mart-search {
-    padding-right: 10px;
+    padding-inline-end: 10px;
   }
 
   .emoji-mart-search-icon {
-    right: 10px + 5px;
+    inset-inline-end: 10px + 5px;
   }
 }
 
@@ -5619,7 +5668,7 @@ a.status-card.compact:hover {
 
 .report-modal__comment {
   padding: 20px;
-  border-right: 1px solid $ui-secondary-color;
+  border-inset-inline-end: 1px solid $ui-secondary-color;
   max-width: 320px;
 
   p {
@@ -5717,7 +5766,7 @@ a.status-card.compact:hover {
         }
 
         button:first-child {
-          margin-right: 10px;
+          margin-inline-end: 10px;
         }
       }
     }
@@ -5781,7 +5830,7 @@ a.status-card.compact:hover {
     border: 1px solid darken($simple-background-color, 14%);
     border-radius: 4px;
     padding: 6px 10px;
-    padding-right: 30px;
+    padding-inline-end: 30px;
   }
 }
 
@@ -5805,7 +5854,7 @@ a.status-card.compact:hover {
     &__label {
       color: $inverted-text-color;
       margin: 0;
-      margin-left: 8px;
+      margin-inline-start: 8px;
     }
   }
 }
@@ -5816,7 +5865,7 @@ a.status-card.compact:hover {
   .report-modal__close {
     position: absolute;
     top: 10px;
-    right: 10px;
+    inset-inline-end: 10px;
   }
 }
 
@@ -5867,7 +5916,7 @@ a.status-card.compact:hover {
   height: 3px;
   position: fixed;
   top: 0;
-  left: 0;
+  inset-inline-start: 0;
   z-index: 9999;
 }
 
@@ -5877,7 +5926,7 @@ a.status-card.compact:hover {
   color: $primary-text-color;
   background: rgba($base-overlay-background, 0.5);
   bottom: 6px;
-  left: 6px;
+  inset-inline-start: 6px;
   padding: 2px 6px;
   border-radius: 2px;
   font-size: 11px;
@@ -5910,7 +5959,7 @@ a.status-card.compact:hover {
     color: $dark-text-color;
     padding: 8px 18px;
     cursor: default;
-    border-right: 1px solid lighten($ui-base-color, 8%);
+    border-inset-inline-end: 1px solid lighten($ui-base-color, 8%);
     display: flex;
     flex-direction: column;
     align-items: center;
@@ -5925,7 +5974,7 @@ a.status-card.compact:hover {
   &__list {
     list-style: none;
     padding: 4px 0;
-    padding-left: 8px;
+    padding-inline-start: 8px;
     display: flex;
     flex-direction: column;
     justify-content: center;
@@ -5975,7 +6024,7 @@ a.status-card.compact:hover {
   border: 0;
   box-sizing: border-box;
   display: block;
-  float: left;
+  float: inline-start;
   position: relative;
   border-radius: 4px;
   overflow: hidden;
@@ -6013,7 +6062,7 @@ a.status-card.compact:hover {
   object-fit: cover;
   position: absolute;
   top: 0;
-  left: 0;
+  inset-inline-start: 0;
   z-index: 0;
   background: $base-overlay-background;
 
@@ -6181,8 +6230,8 @@ a.status-card.compact:hover {
     position: absolute;
     z-index: 2;
     bottom: 0;
-    left: 0;
-    right: 0;
+    inset-inline-start: 0;
+    inset-inline-end: 0;
     box-sizing: border-box;
     background: linear-gradient(
       0deg,
@@ -6210,7 +6259,7 @@ a.status-card.compact:hover {
     display: none;
     position: absolute;
     top: 0;
-    left: 0;
+    inset-inline-start: 0;
     width: 100%;
     height: 100%;
     z-index: 4;
@@ -6326,7 +6375,7 @@ a.status-card.compact:hover {
     &.active {
       overflow: visible;
       width: 50px;
-      margin-right: 16px;
+      margin-inline-end: 16px;
     }
 
     &::before {
@@ -6337,7 +6386,7 @@ a.status-card.compact:hover {
       display: block;
       position: absolute;
       height: 4px;
-      left: 0;
+      inset-inline-start: 0;
       top: 50%;
       transform: translate(0, -50%);
     }
@@ -6347,7 +6396,7 @@ a.status-card.compact:hover {
       position: absolute;
       height: 4px;
       border-radius: 4px;
-      left: 0;
+      inset-inline-start: 0;
       top: 50%;
       transform: translate(0, -50%);
       background: lighten($ui-highlight-color, 8%);
@@ -6360,8 +6409,8 @@ a.status-card.compact:hover {
       width: 12px;
       height: 12px;
       top: 50%;
-      left: 0;
-      margin-left: -6px;
+      inset-inline-start: 0;
+      margin-inline-start: -6px;
       transform: translate(0, -50%);
       background: lighten($ui-highlight-color, 8%);
       box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
@@ -6432,7 +6481,7 @@ a.status-card.compact:hover {
       width: 12px;
       height: 12px;
       top: 10px;
-      margin-left: -6px;
+      margin-inline-start: -6px;
       background: lighten($ui-highlight-color, 8%);
       box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
 
@@ -6508,7 +6557,7 @@ a.status-card.compact:hover {
   &__icons {
     position: absolute;
     top: 50%;
-    left: 50%;
+    inset-inline-start: 50%;
     transform: translate(-50%, -50%);
     font-size: 24px;
   }
@@ -6551,7 +6600,7 @@ a.status-card.compact:hover {
         content: '';
         position: absolute;
         bottom: 0;
-        left: 50%;
+        inset-inline-start: 50%;
         width: 0;
         height: 0;
         transform: translateX(-50%);
@@ -6627,7 +6676,7 @@ a.status-card.compact:hover {
     width: 18px;
     height: 18px;
     flex: 0 0 auto;
-    margin-right: 10px;
+    margin-inline-end: 10px;
     top: -1px;
     border-radius: 50%;
     vertical-align: middle;
@@ -6704,10 +6753,10 @@ noscript {
 
   .navigation-bar {
     & > a:first-child {
-      will-change: margin-top, margin-left, margin-right, width;
+      will-change: margin-top, margin-inline-start, margin-inline-end, width;
       transition: margin-top $duration $delay,
-        margin-left $duration ($duration + $delay),
-        margin-right $duration ($duration + $delay);
+        margin-inline-start $duration ($duration + $delay),
+        margin-inline-end $duration ($duration + $delay);
     }
 
     & > .navigation-bar__profile-edit {
@@ -6889,7 +6938,7 @@ noscript {
   cursor: pointer;
   position: absolute;
   top: 0;
-  left: 0;
+  inset-inline-start: 0;
   width: 100%;
   height: 100%;
   background: rgba($base-overlay-background, 0.5);
@@ -7027,13 +7076,13 @@ noscript {
     width: 100%;
     height: 100%;
     top: 0;
-    left: 0;
+    inset-inline-start: 0;
   }
 
   &__preview {
     position: absolute;
     bottom: 10px;
-    right: 10px;
+    inset-inline-end: 10px;
     z-index: 2;
     cursor: move;
     transition: opacity 0.1s ease;
@@ -7110,7 +7159,7 @@ noscript {
   &__info {
     position: absolute;
     top: 10px;
-    left: 10px;
+    inset-inline-start: 10px;
   }
 
   &__image {
@@ -7153,7 +7202,7 @@ noscript {
     padding-top: 10px;
     gap: 8px;
     overflow: hidden;
-    margin-left: -2px; // aligns the pfp with content below
+    margin-inline-start: -2px; // aligns the pfp with content below
 
     &__buttons {
       display: flex;
@@ -7356,6 +7405,19 @@ noscript {
   }
 }
 
+.verified-badge {
+  display: inline-flex;
+  align-items: center;
+  color: $valid-value-color;
+  gap: 4px;
+
+  a {
+    color: inherit;
+    font-weight: 500;
+    text-decoration: none;
+  }
+}
+
 .trends {
   &__header {
     color: $dark-text-color;
@@ -7368,7 +7430,7 @@ noscript {
 
     .fa {
       display: inline-block;
-      margin-right: 5px;
+      margin-inline-end: 5px;
     }
   }
 
@@ -7418,7 +7480,7 @@ noscript {
       flex: 0 0 auto;
       font-size: 24px;
       font-weight: 500;
-      text-align: right;
+      text-align: end;
       color: $secondary-text-color;
       text-decoration: none;
     }
@@ -7523,7 +7585,7 @@ noscript {
   &__content {
     flex: 1 1 auto;
     padding: 10px 5px;
-    padding-right: 15px;
+    padding-inline-end: 15px;
     overflow: hidden;
 
     &__info {
@@ -7536,7 +7598,7 @@ noscript {
     &__relative-time {
       font-size: 15px;
       color: $darker-text-color;
-      padding-left: 15px;
+      padding-inline-start: 15px;
     }
 
     &__names {
@@ -7626,13 +7688,13 @@ noscript {
       display: block;
       font-weight: 500;
       margin-bottom: 10px;
-      padding-right: 18px;
+      padding-inline-end: 18px;
     }
 
     &__unread {
       position: absolute;
       top: 19px;
-      right: 19px;
+      inset-inline-end: 19px;
       display: block;
       background: $highlight-text-color;
       border-radius: 50%;
@@ -7646,7 +7708,7 @@ noscript {
     color: $darker-text-color;
     position: absolute;
     bottom: 3px;
-    right: 0;
+    inset-inline-end: 0;
   }
 }
 
@@ -7663,7 +7725,7 @@ noscript {
   flex-wrap: wrap;
   align-items: center;
   margin-top: 15px;
-  margin-left: -2px;
+  margin-inline-start: -2px;
   width: calc(100% - (90px - 33px));
 
   &__item {
@@ -7704,7 +7766,7 @@ noscript {
       font-size: 13px;
       font-weight: 500;
       text-align: center;
-      margin-left: 6px;
+      margin-inline-start: 6px;
       color: $darker-text-color;
     }
 
@@ -7783,10 +7845,10 @@ noscript {
       content: '';
       position: absolute;
       top: 0;
-      left: 0;
+      inset-inline-start: 0;
       width: 100%;
       height: 100%;
-      border-left: 4px solid $highlight-text-color;
+      border-inset-inline-start: 4px solid $highlight-text-color;
       pointer-events: none;
     }
   }
@@ -7795,7 +7857,7 @@ noscript {
 .picture-in-picture {
   position: fixed;
   bottom: 20px;
-  right: 20px;
+  inset-inline-end: 20px;
   width: 300px;
 
   &__footer {
@@ -7821,7 +7883,7 @@ noscript {
     }
 
     .account__avatar {
-      margin-right: 10px;
+      margin-inline-end: 10px;
     }
 
     .display-name {
@@ -7887,7 +7949,7 @@ noscript {
   &__close {
     position: absolute;
     top: 10px;
-    right: 10px;
+    inset-inline-end: 10px;
   }
 
   h2 {
@@ -7922,7 +7984,7 @@ noscript {
 
   .search .fa {
     top: 10px;
-    right: 10px;
+    inset-inline-end: 10px;
     color: $dark-text-color;
   }
 
@@ -8006,7 +8068,7 @@ noscript {
       object-fit: fill;
       position: absolute;
       top: 0;
-      left: 0;
+      inset-inline-start: 0;
       z-index: 0;
 
       &--hidden {
@@ -8092,7 +8154,7 @@ noscript {
   }
 
   .account__avatar-wrapper {
-    margin-left: 0;
+    margin-inline-start: 0;
   }
 
   .spacer {
@@ -8312,7 +8374,7 @@ noscript {
     &::before {
       content: counter(list-counter) '.';
       position: absolute;
-      left: 0;
+      inset-inline-start: 0;
     }
   }
 
@@ -8324,13 +8386,13 @@ noscript {
     width: 0.375em;
     height: 0.375em;
     top: 0.5em;
-    left: 0.25em;
+    inset-inline-start: 0.25em;
   }
 
   ul > li,
   ol > li {
     position: relative;
-    padding-left: 1.75em;
+    padding-inline-start: 1.75em;
   }
 
   & > ul > li p {
@@ -8463,7 +8525,7 @@ noscript {
   &__preview {
     position: absolute;
     top: 0;
-    left: 0;
+    inset-inline-start: 0;
     width: 100%;
     height: 100%;
     object-fit: cover;
@@ -8669,7 +8731,7 @@ noscript {
   }
 
   .account__avatar-wrapper {
-    margin-left: 0;
+    margin-inline-start: 0;
   }
 
   .account__relationship {
diff --git a/app/javascript/styles/mastodon/containers.scss b/app/javascript/styles/mastodon/containers.scss
index b49b93984..fb71ad034 100644
--- a/app/javascript/styles/mastodon/containers.scss
+++ b/app/javascript/styles/mastodon/containers.scss
@@ -18,7 +18,7 @@
 
     .logo {
       height: 42px;
-      margin-right: 10px;
+      margin-inline-end: 10px;
     }
 
     a {
@@ -73,7 +73,7 @@
   .avatar {
     width: 40px;
     height: 40px;
-    margin-right: 10px;
+    margin-inline-end: 10px;
 
     img {
       width: 100%;
@@ -101,6 +101,6 @@
     display: block;
     font-size: 32px;
     line-height: 40px;
-    margin-left: 10px;
+    margin-inline-start: 10px;
   }
 }
diff --git a/app/javascript/styles/mastodon/emoji_picker.scss b/app/javascript/styles/mastodon/emoji_picker.scss
index 0d7a7df2e..c7247c3a5 100644
--- a/app/javascript/styles/mastodon/emoji_picker.scss
+++ b/app/javascript/styles/mastodon/emoji_picker.scss
@@ -71,7 +71,7 @@
 .emoji-mart-anchor-bar {
   position: absolute;
   bottom: -5px;
-  left: 0;
+  inset-inline-start: 0;
   width: 100%;
   height: 4px;
   background-color: $highlight-text-color;
@@ -106,7 +106,7 @@
 
 .emoji-mart-search {
   padding: 10px;
-  padding-right: 45px;
+  padding-inline-end: 45px;
   background: $simple-background-color;
   position: relative;
 
@@ -114,7 +114,7 @@
     font-size: 16px;
     font-weight: 400;
     padding: 7px 9px;
-    padding-right: 25px;
+    padding-inline-end: 25px;
     font-family: inherit;
     display: block;
     width: 100%;
@@ -142,7 +142,7 @@
 .emoji-mart-search-icon {
   position: absolute;
   top: 18px;
-  right: 45px + 5px;
+  inset-inline-end: 45px + 5px;
   z-index: 2;
   padding: 2px 5px 1px;
   border: 0;
@@ -177,7 +177,7 @@
     content: '';
     position: absolute;
     top: 0;
-    left: 0;
+    inset-inline-start: 0;
     width: 100%;
     height: 100%;
     background-color: rgba($ui-secondary-color, 0.7);
diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss
index e4539deff..7d4bde5e9 100644
--- a/app/javascript/styles/mastodon/forms.scss
+++ b/app/javascript/styles/mastodon/forms.scss
@@ -54,12 +54,12 @@ code {
 
       .radio > label {
         position: relative;
-        padding-left: 28px;
+        padding-inline-start: 28px;
 
         input {
           position: absolute;
           top: -2px;
-          left: 0;
+          inset-inline-start: 0;
         }
       }
     }
@@ -79,7 +79,7 @@ code {
 
       .label_input,
       .hint {
-        padding-left: 28px;
+        padding-inline-start: 28px;
       }
 
       .label_input__wrapper {
@@ -89,7 +89,7 @@ code {
       label.checkbox {
         position: absolute;
         top: 2px;
-        left: 0;
+        inset-inline-start: 0;
       }
 
       label a {
@@ -159,7 +159,7 @@ code {
 
     li {
       list-style: disc;
-      margin-left: 18px;
+      margin-inline-start: 18px;
     }
   }
 
@@ -225,7 +225,7 @@ code {
 
     &.select .hint {
       margin-top: 6px;
-      margin-left: 150px;
+      margin-inline-start: 150px;
     }
   }
 
@@ -380,13 +380,13 @@ code {
         width: auto;
         position: relative;
         padding-top: 5px;
-        padding-left: 25px;
+        padding-inline-start: 25px;
         flex: 1 1 auto;
       }
 
       input[type='checkbox'] {
         position: absolute;
-        left: 0;
+        inset-inline-start: 0;
         top: 5px;
         margin: 0;
       }
@@ -517,10 +517,10 @@ code {
     font-weight: 500;
     outline: 0;
     margin-bottom: 10px;
-    margin-right: 10px;
+    margin-inline-end: 10px;
 
     &:last-child {
-      margin-right: 0;
+      margin-inline-end: 0;
     }
 
     &:active,
@@ -572,8 +572,8 @@ code {
       no-repeat right 8px center / auto 16px;
     border: 1px solid darken($ui-base-color, 14%);
     border-radius: 4px;
-    padding-left: 10px;
-    padding-right: 30px;
+    padding-inline-start: 10px;
+    padding-inline-end: 30px;
     height: 41px;
   }
 
@@ -588,7 +588,7 @@ code {
 
     &__append {
       position: absolute;
-      right: 3px;
+      inset-inline-end: 3px;
       top: 1px;
       padding: 10px;
       padding-bottom: 9px;
@@ -606,7 +606,7 @@ code {
         display: block;
         position: absolute;
         top: 0;
-        right: 0;
+        inset-inline-end: 0;
         bottom: 1px;
         width: 5px;
         background-image: linear-gradient(
@@ -780,7 +780,7 @@ code {
 
   li {
     display: inline-block;
-    margin-right: 10px;
+    margin-inline-end: 10px;
   }
 
   a {
@@ -939,7 +939,7 @@ code {
 
   .actions {
     padding: 30px 0;
-    padding-right: 20px;
+    padding-inline-end: 20px;
     flex: 0 0 auto;
   }
 }
@@ -992,7 +992,7 @@ code {
   border-radius: 4px;
   display: flex;
   align-items: center;
-  padding-right: 4px;
+  padding-inline-end: 4px;
   position: relative;
   top: 1px;
   transition: border-color 300ms linear;
diff --git a/app/javascript/styles/mastodon/modal.scss b/app/javascript/styles/mastodon/modal.scss
index 6170877b2..29b1f162b 100644
--- a/app/javascript/styles/mastodon/modal.scss
+++ b/app/javascript/styles/mastodon/modal.scss
@@ -25,7 +25,7 @@
       height: 100%;
       position: absolute;
       bottom: 0;
-      left: 0;
+      inset-inline-start: 0;
     }
   }
 }
diff --git a/app/javascript/styles/mastodon/polls.scss b/app/javascript/styles/mastodon/polls.scss
index b30932e04..bdb87d7cf 100644
--- a/app/javascript/styles/mastodon/polls.scss
+++ b/app/javascript/styles/mastodon/polls.scss
@@ -269,7 +269,7 @@
     border: 1px solid darken($simple-background-color, 14%);
     border-radius: 4px;
     padding: 6px 10px;
-    padding-right: 30px;
+    padding-inline-end: 30px;
   }
 
   .icon-button.disabled {
diff --git a/app/javascript/styles/mastodon/rich_text.scss b/app/javascript/styles/mastodon/rich_text.scss
index 35901984b..aa41e4ad3 100644
--- a/app/javascript/styles/mastodon/rich_text.scss
+++ b/app/javascript/styles/mastodon/rich_text.scss
@@ -13,8 +13,8 @@
   }
 
   blockquote {
-    padding-left: 10px;
-    border-left: 3px solid $darker-text-color;
+    padding-inline-start: 10px;
+    border-inset-inline-start: 3px solid $darker-text-color;
     color: $darker-text-color;
     white-space: normal;
 
@@ -40,7 +40,7 @@
 
   ul,
   ol {
-    margin-left: 2em;
+    margin-inline-start: 2em;
 
     p {
       margin: 0;
diff --git a/app/javascript/styles/mastodon/rtl.scss b/app/javascript/styles/mastodon/rtl.scss
index e60087dab..726135c58 100644
--- a/app/javascript/styles/mastodon/rtl.scss
+++ b/app/javascript/styles/mastodon/rtl.scss
@@ -1,273 +1,30 @@
 body.rtl {
   direction: rtl;
 
-  .column-header > button {
-    text-align: right;
-    padding-left: 0;
-    padding-right: 15px;
-  }
-
-  .radio-button__input {
-    margin-right: 0;
-    margin-left: 10px;
-  }
-
-  .display-name,
-  .announcements__item {
-    text-align: right;
-  }
-
-  .announcements__item__range {
-    padding-right: 0;
-    padding-left: 18px;
-  }
-
   .reactions-bar {
-    margin-left: auto;
-    margin-right: -2px;
     direction: rtl;
   }
 
-  .reactions-bar__item__count {
-    margin-left: 0;
-    margin-right: 6px;
-  }
-
-  .announcements__pagination {
-    right: auto;
-    left: 0;
-  }
-
-  .notification__message {
-    margin-left: 0;
-    margin-right: 68px;
-  }
-
   .announcements__mastodon,
   .drawer__inner__mastodon > img {
     transform: scaleX(-1);
   }
 
-  .notification__favourite-icon-wrapper {
-    left: auto;
-    right: -26px;
-  }
-
-  .column-link__icon,
-  .column-header__icon {
-    margin-right: 0;
-    margin-left: 5px;
-  }
-
-  .compose-form .compose-form__buttons-wrapper .character-counter__wrapper {
-    margin-right: 0;
-    margin-left: 4px;
-  }
-
-  .navigation-bar__profile {
-    margin-left: 0;
-    margin-right: 8px;
-  }
-
-  .search__input {
+  .compose-form .autosuggest-textarea__textarea {
     padding-right: 10px;
-    padding-left: 30px;
-  }
-
-  .search__icon .fa {
-    right: auto;
-    left: 10px;
+    padding-left: 10px + 22px;
   }
 
   .columns-area {
     direction: rtl;
   }
 
-  .column-header__buttons {
-    left: 0;
-    right: auto;
-    margin-left: 0;
-    margin-right: -15px;
-  }
-
-  .column-inline-form .icon-button {
-    margin-left: 0;
-    margin-right: 5px;
-  }
-
-  .column-header__links .text-btn {
-    margin-left: 10px;
-    margin-right: 0;
-  }
-
-  .account__avatar-wrapper {
-    float: right;
-  }
-
-  .column-header__back-button {
-    padding-left: 5px;
-    padding-right: 0;
-  }
-
-  .column-header__setting-arrows {
-    float: left;
-
-    .column-header__setting-btn {
-      &:first-child {
-        padding-left: 7px;
-        padding-right: 5px;
-      }
-
-      &:last-child {
-        padding-right: 7px;
-        padding-left: 5px;
-        margin-right: 5px;
-        margin-left: 0;
-      }
-    }
-  }
-
-  .setting-toggle__label {
-    margin-left: 0;
-    margin-right: 8px;
-  }
-
-  .account__header__tabs__buttons > .icon-button {
-    margin-right: 0;
-    margin-left: 8px;
-  }
-
-  .account__avatar-overlay-overlay {
-    right: auto;
-    left: 0;
-  }
-
-  .column-back-button--slim-button {
-    right: auto;
-    left: 0;
-  }
-
-  .status__action-bar {
-    &__counter {
-      margin-right: 0;
-      margin-left: 11px;
-
-      .status__action-bar-button {
-        margin-right: 0;
-        margin-left: 4px;
-      }
-    }
-  }
-
-  .privacy-dropdown__dropdown {
-    margin-left: 0;
-    margin-right: 40px;
-  }
-
-  .privacy-dropdown__option__icon {
-    margin-left: 10px;
-    margin-right: 0;
-  }
-
-  .picture-in-picture__header__account .display-name,
-  .detailed-status__display-name .display-name {
-    text-align: right;
-  }
-
-  .detailed-status__display-avatar {
-    margin-right: 0;
-    margin-left: 10px;
-  }
-
-  .picture-in-picture__header__account .account__avatar {
-    margin-right: 0;
-    margin-left: 10px;
-  }
-
-  .icon-button__counter {
-    margin-left: 0;
-    margin-right: 4px;
-  }
-
-  .notifications-permission-banner__close {
-    right: auto;
-    left: 10px;
-  }
-
-  .detailed-status__favorites,
-  .detailed-status__reblogs {
-    margin-left: 0;
-    margin-right: 6px;
-  }
-
-  .fa-ul {
-    margin-left: 2.14285714em;
-  }
-
-  .fa-li {
-    left: auto;
-    right: -2.14285714em;
-  }
-
   .admin-wrapper {
     direction: rtl;
   }
 
-  .admin-wrapper .sidebar ul a i.fa,
-  a.table-action-link i.fa {
-    margin-right: 0;
-    margin-left: 5px;
-  }
-
-  .simple_form .check_boxes .checkbox label {
-    padding-left: 0;
-    padding-right: 25px;
-  }
-
-  .simple_form .input.with_label.boolean label.checkbox {
-    padding-left: 25px;
-    padding-right: 0;
-  }
-
-  .simple_form .check_boxes .checkbox input[type='checkbox'],
-  .simple_form .input.boolean input[type='checkbox'] {
-    left: auto;
-    right: 0;
-  }
-
-  .simple_form .input.radio_buttons .radio {
-    left: auto;
-    right: 0;
-  }
-
-  .simple_form .input.radio_buttons .radio > label {
-    padding-right: 28px;
-    padding-left: 0;
-  }
-
-  .simple_form .input-with-append .input input {
-    padding-left: 142px;
-    padding-right: 0;
-  }
-
-  .simple_form .input.boolean label.checkbox {
-    left: auto;
-    right: 0;
-  }
-
-  .simple_form .input.boolean .label_input,
-  .simple_form .input.boolean .hint {
-    padding-left: 0;
-    padding-right: 28px;
-  }
-
   .simple_form .label_input__append {
-    right: auto;
-    left: 3px;
-
     &::after {
-      right: auto;
-      left: 0;
       background-image: linear-gradient(
         to left,
         rgba(darken($ui-base-color, 10%), 0),
@@ -282,49 +39,6 @@ body.rtl {
       no-repeat left 8px center / auto 16px;
   }
 
-  .table th,
-  .table td {
-    text-align: right;
-  }
-
-  .filters .filter-subset {
-    margin-right: 0;
-    margin-left: 45px;
-  }
-
-  @media screen and (min-width: 631px) {
-    .column,
-    .drawer {
-      padding-left: 5px;
-      padding-right: 5px;
-
-      &:first-child {
-        padding-left: 5px;
-        padding-right: 10px;
-      }
-    }
-
-    .columns-area > div {
-      .column,
-      .drawer {
-        padding-left: 5px;
-        padding-right: 5px;
-      }
-    }
-  }
-
-  .columns-area--mobile .column,
-  .columns-area--mobile .drawer {
-    padding-left: 0;
-    padding-right: 0;
-  }
-
-  .card__bar .display-name {
-    margin-left: 0;
-    margin-right: 15px;
-    text-align: right;
-  }
-
   .fa-chevron-left::before {
     content: '\F054';
   }
@@ -332,19 +46,4 @@ body.rtl {
   .fa-chevron-right::before {
     content: '\F053';
   }
-
-  .column-back-button__icon {
-    margin-right: 0;
-    margin-left: 5px;
-  }
-
-  .simple_form .input.radio_buttons .radio > label input {
-    left: auto;
-    right: 0;
-  }
-
-  .picture-in-picture {
-    right: auto;
-    left: 20px;
-  }
 }
diff --git a/app/javascript/styles/mastodon/statuses.scss b/app/javascript/styles/mastodon/statuses.scss
index a42f1f42c..6c9ea916a 100644
--- a/app/javascript/styles/mastodon/statuses.scss
+++ b/app/javascript/styles/mastodon/statuses.scss
@@ -97,7 +97,7 @@
     width: 20px;
     height: auto;
     vertical-align: middle;
-    margin-right: 5px;
+    margin-inline-end: 5px;
     fill: $primary-text-color;
   }
 
@@ -162,7 +162,7 @@ a.button.logo-button {
     min-height: 48px + 2px;
 
     &__avatar {
-      left: 15px;
+      inset-inline-start: 15px;
       top: 17px;
 
       .account__avatar {
@@ -176,12 +176,12 @@ a.button.logo-button {
     }
 
     &__prepend {
-      margin-left: 48px + 15px * 2;
+      margin-inline-start: 48px + 15px * 2;
       padding-top: 15px;
     }
 
     &__prepend-icon-wrapper {
-      left: -32px;
+      inset-inline-start: -32px;
     }
 
     .media-gallery,
diff --git a/app/javascript/styles/mastodon/tables.scss b/app/javascript/styles/mastodon/tables.scss
index b644b38f1..fb1ff0781 100644
--- a/app/javascript/styles/mastodon/tables.scss
+++ b/app/javascript/styles/mastodon/tables.scss
@@ -10,7 +10,7 @@
     line-height: 18px;
     vertical-align: top;
     border-top: 1px solid $ui-base-color;
-    text-align: left;
+    text-align: start;
     background: darken($ui-base-color, 4%);
   }
 
@@ -91,12 +91,12 @@
 
       &:first-child {
         border-radius: 4px 0 0;
-        border-left: 1px solid darken($ui-base-color, 8%);
+        border-inset-inline-start: 1px solid darken($ui-base-color, 8%);
       }
 
       &:last-child {
         border-radius: 0 4px 0 0;
-        border-right: 1px solid darken($ui-base-color, 8%);
+        border-inset-inline-end: 1px solid darken($ui-base-color, 8%);
       }
     }
   }
@@ -125,7 +125,7 @@ button.table-action-link,
 a.table-action-link {
   text-decoration: none;
   display: inline-block;
-  margin-right: 5px;
+  margin-inline-end: 5px;
   padding: 0 10px;
   color: $darker-text-color;
   font-weight: 500;
@@ -136,11 +136,11 @@ a.table-action-link {
 
   i.fa {
     font-weight: 400;
-    margin-right: 5px;
+    margin-inline-end: 5px;
   }
 
   &:first-child {
-    padding-left: 0;
+    padding-inline-start: 0;
   }
 }
 
@@ -172,7 +172,7 @@ a.table-action-link {
     &__actions,
     &__content {
       padding: 8px 0;
-      padding-right: 16px;
+      padding-inline-end: 16px;
       flex: 1 1 auto;
     }
   }
@@ -188,8 +188,8 @@ a.table-action-link {
     align-items: center;
 
     &__actions {
-      text-align: right;
-      padding-right: 16px - 5px;
+      text-align: end;
+      padding-inline-end: 16px - 5px;
     }
   }
 
@@ -296,7 +296,7 @@ a.table-action-link {
         display: flex;
         justify-content: center;
         align-items: center;
-        margin-right: 10px;
+        margin-inline-end: 10px;
 
         .emojione {
           width: 32px;
@@ -315,7 +315,7 @@ a.table-action-link {
 
       &__extra {
         flex: 0 0 auto;
-        text-align: right;
+        text-align: end;
         color: $darker-text-color;
         font-weight: 500;
       }
diff --git a/app/javascript/styles/mastodon/widgets.scss b/app/javascript/styles/mastodon/widgets.scss
index ef7bfc6de..1f69f0cf0 100644
--- a/app/javascript/styles/mastodon/widgets.scss
+++ b/app/javascript/styles/mastodon/widgets.scss
@@ -39,8 +39,8 @@
       width: 20px;
       height: 20px;
       margin: -3px 0 0;
-      margin-left: 0.075em;
-      margin-right: 0.075em;
+      margin-inline-start: 0.075em;
+      margin-inline-end: 0.075em;
     }
 
     p {
@@ -171,7 +171,7 @@
     margin-bottom: 15px;
 
     .fa {
-      margin-right: 5px;
+      margin-inline-end: 5px;
       color: $darker-text-color;
     }
   }
@@ -284,7 +284,7 @@
     }
 
     .trends__item__current {
-      padding-right: 0;
+      padding-inline-end: 0;
     }
   }
 }
@@ -309,7 +309,7 @@
     padding: 10px;
 
     &:first-child {
-      text-align: left;
+      text-align: start;
     }
   }
 
@@ -340,9 +340,9 @@
 
   tbody td.accounts-table__extra {
     width: 120px;
-    text-align: right;
+    text-align: end;
     color: $darker-text-color;
-    padding-right: 16px;
+    padding-inline-end: 16px;
 
     a {
       text-decoration: none;
@@ -363,7 +363,7 @@
 
   tbody td.accounts-table__interrelationships {
     width: 21px;
-    padding-right: 16px;
+    padding-inline-end: 16px;
   }
 
   .fa {
diff --git a/app/lib/admin/metrics/retention.rb b/app/lib/admin/metrics/retention.rb
index f6135ac1e..9bd47c58e 100644
--- a/app/lib/admin/metrics/retention.rb
+++ b/app/lib/admin/metrics/retention.rb
@@ -42,25 +42,54 @@ class Admin::Metrics::Retention
   end
 
   def perform_query
-    sql = <<-SQL.squish
+    report_rows.each_with_object([]) do |row, arr|
+      current_cohort = arr.last
+
+      if current_cohort.nil? || current_cohort.period != row['cohort_period']
+        current_cohort = Cohort.new(period: row['cohort_period'], frequency: @frequency, data: [])
+        arr << current_cohort
+      end
+
+      value, rate = row['retention_value_and_rate'].delete('{}').split(',')
+
+      current_cohort.data << CohortData.new(
+        date: row['retention_period'],
+        rate: rate.to_f,
+        value: value.to_s
+      )
+    end
+  end
+
+  def report_rows
+    ActiveRecord::Base.connection.select_all(sanitized_sql_string)
+  end
+
+  def sanitized_sql_string
+    ActiveRecord::Base.sanitize_sql_array(
+      [sql_query_string, { start_at: @start_at, end_at: @end_at, frequency: @frequency }]
+    )
+  end
+
+  def sql_query_string
+    <<~SQL.squish
       SELECT axis.*, (
         WITH new_users AS (
           SELECT users.id
           FROM users
-          WHERE date_trunc($3, users.created_at)::date = axis.cohort_period
+          WHERE date_trunc(:frequency, users.created_at)::date = axis.cohort_period
         ),
         retained_users AS (
           SELECT users.id
           FROM users
           INNER JOIN new_users on new_users.id = users.id
-          WHERE date_trunc($3, users.current_sign_in_at) >= axis.retention_period
+          WHERE date_trunc(:frequency, users.current_sign_in_at) >= axis.retention_period
         )
         SELECT ARRAY[count(*), (count(*))::float / (SELECT GREATEST(count(*), 1) FROM new_users)] AS retention_value_and_rate
         FROM retained_users
       )
       FROM (
         WITH cohort_periods AS (
-          SELECT generate_series(date_trunc($3, $1::timestamp)::date, date_trunc($3, $2::timestamp)::date, ('1 ' || $3)::interval) AS cohort_period
+          SELECT generate_series(date_trunc(:frequency, :start_at::timestamp)::date, date_trunc(:frequency, :end_at::timestamp)::date, ('1 ' || :frequency)::interval) AS cohort_period
         ),
         retention_periods AS (
           SELECT cohort_period AS retention_period FROM cohort_periods
@@ -70,24 +99,5 @@ class Admin::Metrics::Retention
         WHERE retention_period >= cohort_period
       ) as axis
     SQL
-
-    rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, @start_at], [nil, @end_at], [nil, @frequency]])
-
-    rows.each_with_object([]) do |row, arr|
-      current_cohort = arr.last
-
-      if current_cohort.nil? || current_cohort.period != row['cohort_period']
-        current_cohort = Cohort.new(period: row['cohort_period'], frequency: @frequency, data: [])
-        arr << current_cohort
-      end
-
-      value, rate = row['retention_value_and_rate'].delete('{}').split(',')
-
-      current_cohort.data << CohortData.new(
-        date: row['retention_period'],
-        rate: rate.to_f,
-        value: value.to_s
-      )
-    end
   end
 end
diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb
deleted file mode 100644
index 260077a1c..000000000
--- a/app/lib/user_settings_decorator.rb
+++ /dev/null
@@ -1,180 +0,0 @@
-# frozen_string_literal: true
-
-class UserSettingsDecorator
-  attr_reader :user, :settings
-
-  def initialize(user)
-    @user = user
-  end
-
-  def update(settings)
-    @settings = settings
-    process_update
-  end
-
-  private
-
-  def process_update
-    user.settings['notification_emails'] = merged_notification_emails if change?('notification_emails')
-    user.settings['interactions']        = merged_interactions if change?('interactions')
-    user.settings['default_privacy']     = default_privacy_preference if change?('setting_default_privacy')
-    user.settings['default_sensitive']   = default_sensitive_preference if change?('setting_default_sensitive')
-    user.settings['default_language']    = default_language_preference if change?('setting_default_language')
-    user.settings['unfollow_modal']      = unfollow_modal_preference if change?('setting_unfollow_modal')
-    user.settings['boost_modal']         = boost_modal_preference if change?('setting_boost_modal')
-    user.settings['favourite_modal']     = favourite_modal_preference if change?('setting_favourite_modal')
-    user.settings['delete_modal']        = delete_modal_preference if change?('setting_delete_modal')
-    user.settings['auto_play_gif']       = auto_play_gif_preference if change?('setting_auto_play_gif')
-    user.settings['display_media']       = display_media_preference if change?('setting_display_media')
-    user.settings['expand_spoilers']     = expand_spoilers_preference if change?('setting_expand_spoilers')
-    user.settings['reduce_motion']       = reduce_motion_preference if change?('setting_reduce_motion')
-    user.settings['disable_swiping']     = disable_swiping_preference if change?('setting_disable_swiping')
-    user.settings['system_font_ui']      = system_font_ui_preference if change?('setting_system_font_ui')
-    user.settings['system_emoji_font']   = system_emoji_font_preference if change?('setting_system_emoji_font')
-    user.settings['noindex']             = noindex_preference if change?('setting_noindex')
-    user.settings['hide_followers_count'] = hide_followers_count_preference if change?('setting_hide_followers_count')
-    user.settings['flavour']             = flavour_preference if change?('setting_flavour')
-    user.settings['skin']                = skin_preference if change?('setting_skin')
-    user.settings['aggregate_reblogs']   = aggregate_reblogs_preference if change?('setting_aggregate_reblogs')
-    user.settings['show_application']    = show_application_preference if change?('setting_show_application')
-    user.settings['advanced_layout']     = advanced_layout_preference if change?('setting_advanced_layout')
-    user.settings['default_content_type']= default_content_type_preference if change?('setting_default_content_type')
-    user.settings['use_blurhash']        = use_blurhash_preference if change?('setting_use_blurhash')
-    user.settings['use_pending_items']   = use_pending_items_preference if change?('setting_use_pending_items')
-    user.settings['trends']              = trends_preference if change?('setting_trends')
-    user.settings['crop_images']         = crop_images_preference if change?('setting_crop_images')
-    user.settings['always_send_emails']  = always_send_emails_preference if change?('setting_always_send_emails')
-  end
-
-  def merged_notification_emails
-    user.settings['notification_emails'].merge coerced_settings('notification_emails').to_h
-  end
-
-  def merged_interactions
-    user.settings['interactions'].merge coerced_settings('interactions').to_h
-  end
-
-  def default_privacy_preference
-    settings['setting_default_privacy']
-  end
-
-  def default_sensitive_preference
-    boolean_cast_setting 'setting_default_sensitive'
-  end
-
-  def unfollow_modal_preference
-    boolean_cast_setting 'setting_unfollow_modal'
-  end
-
-  def boost_modal_preference
-    boolean_cast_setting 'setting_boost_modal'
-  end
-
-  def favourite_modal_preference
-    boolean_cast_setting 'setting_favourite_modal'
-  end
-
-  def delete_modal_preference
-    boolean_cast_setting 'setting_delete_modal'
-  end
-
-  def system_font_ui_preference
-    boolean_cast_setting 'setting_system_font_ui'
-  end
-
-  def system_emoji_font_preference
-    boolean_cast_setting 'setting_system_emoji_font'
-  end
-
-  def auto_play_gif_preference
-    boolean_cast_setting 'setting_auto_play_gif'
-  end
-
-  def display_media_preference
-    settings['setting_display_media']
-  end
-
-  def expand_spoilers_preference
-    boolean_cast_setting 'setting_expand_spoilers'
-  end
-
-  def reduce_motion_preference
-    boolean_cast_setting 'setting_reduce_motion'
-  end
-
-  def disable_swiping_preference
-    boolean_cast_setting 'setting_disable_swiping'
-  end
-
-  def noindex_preference
-    boolean_cast_setting 'setting_noindex'
-  end
-
-  def flavour_preference
-    settings['setting_flavour']
-  end
-
-  def skin_preference
-    settings['setting_skin']
-  end
-
-  def hide_followers_count_preference
-    boolean_cast_setting 'setting_hide_followers_count'
-  end
-
-  def show_application_preference
-    boolean_cast_setting 'setting_show_application'
-  end
-
-  def default_language_preference
-    settings['setting_default_language']
-  end
-
-  def aggregate_reblogs_preference
-    boolean_cast_setting 'setting_aggregate_reblogs'
-  end
-
-  def advanced_layout_preference
-    boolean_cast_setting 'setting_advanced_layout'
-  end
-
-  def default_content_type_preference
-    settings['setting_default_content_type']
-  end
-
-  def use_blurhash_preference
-    boolean_cast_setting 'setting_use_blurhash'
-  end
-
-  def use_pending_items_preference
-    boolean_cast_setting 'setting_use_pending_items'
-  end
-
-  def trends_preference
-    boolean_cast_setting 'setting_trends'
-  end
-
-  def crop_images_preference
-    boolean_cast_setting 'setting_crop_images'
-  end
-
-  def always_send_emails_preference
-    boolean_cast_setting 'setting_always_send_emails'
-  end
-
-  def boolean_cast_setting(key)
-    ActiveModel::Type::Boolean.new.cast(settings[key])
-  end
-
-  def coerced_settings(key)
-    coerce_values settings.fetch(key, {})
-  end
-
-  def coerce_values(params_hash)
-    params_hash.transform_values { |x| ActiveModel::Type::Boolean.new.cast(x) }
-  end
-
-  def change?(key)
-    !settings[key].nil?
-  end
-end
diff --git a/app/lib/user_settings_serializer.rb b/app/lib/user_settings_serializer.rb
new file mode 100644
index 000000000..10d1be04d
--- /dev/null
+++ b/app/lib/user_settings_serializer.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class UserSettingsSerializer
+  def self.load(value)
+    json = begin
+      if value.blank?
+        {}
+      else
+        Oj.load(value, symbol_keys: true)
+      end
+    end
+
+    UserSettings.new(json)
+  end
+
+  def self.dump(value)
+    Oj.dump(value.as_json)
+  end
+end
diff --git a/app/models/account.rb b/app/models/account.rb
index f2ed871e7..4fc7b9d08 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -53,7 +53,7 @@
 #
 
 class Account < ApplicationRecord
-  self.ignored_columns = %w(
+  self.ignored_columns += %w(
     subscription_expires_at
     secret
     remote_url
diff --git a/app/models/account_stat.rb b/app/models/account_stat.rb
index 834f8ba4c..0fea7732e 100644
--- a/app/models/account_stat.rb
+++ b/app/models/account_stat.rb
@@ -16,7 +16,7 @@
 
 class AccountStat < ApplicationRecord
   self.locking_column = nil
-  self.ignored_columns = %w(lock_version)
+  self.ignored_columns += %w(lock_version)
 
   belongs_to :account, inverse_of: :account_stat
 
diff --git a/app/models/admin/action_log.rb b/app/models/admin/action_log.rb
index 4fa8008f5..f2c121d75 100644
--- a/app/models/admin/action_log.rb
+++ b/app/models/admin/action_log.rb
@@ -17,7 +17,7 @@
 #
 
 class Admin::ActionLog < ApplicationRecord
-  self.ignored_columns = %w(
+  self.ignored_columns += %w(
     recorded_changes
   )
 
diff --git a/app/models/backup.rb b/app/models/backup.rb
index dca06eb58..5feb31d7d 100644
--- a/app/models/backup.rb
+++ b/app/models/backup.rb
@@ -18,6 +18,6 @@
 class Backup < ApplicationRecord
   belongs_to :user, inverse_of: :backups
 
-  has_attached_file :dump, s3_permissions: 'private'
+  has_attached_file :dump, s3_permissions: ->(*) { ENV['S3_PERMISSION'] == '' ? nil : 'private' }
   validates_attachment_content_type :dump, content_type: /\Aapplication/
 end
diff --git a/app/models/concerns/attachmentable.rb b/app/models/concerns/attachmentable.rb
index 01fae4236..d44c22438 100644
--- a/app/models/concerns/attachmentable.rb
+++ b/app/models/concerns/attachmentable.rb
@@ -5,7 +5,7 @@ require 'mime/types/columnar'
 module Attachmentable
   extend ActiveSupport::Concern
 
-  MAX_MATRIX_LIMIT = 16_777_216 # 4096x4096px or approx. 16MB
+  MAX_MATRIX_LIMIT = 33_177_600 # 7680x4320px or approx. 847MB in RAM
   GIF_MATRIX_LIMIT = 921_600    # 1280x720px
 
   # For some file extensions, there exist different content
diff --git a/app/models/concerns/has_user_settings.rb b/app/models/concerns/has_user_settings.rb
new file mode 100644
index 000000000..b3fa1f683
--- /dev/null
+++ b/app/models/concerns/has_user_settings.rb
@@ -0,0 +1,141 @@
+# frozen_string_literal: true
+
+module HasUserSettings
+  extend ActiveSupport::Concern
+
+  included do
+    serialize :settings, UserSettingsSerializer
+  end
+
+  def settings_attributes=(attributes)
+    settings.update(attributes)
+  end
+
+  def prefers_noindex?
+    settings['noindex']
+  end
+
+  def preferred_posting_language
+    valid_locale_cascade(settings['default_language'], locale, I18n.locale)
+  end
+
+  def setting_auto_play_gif
+    settings['web.auto_play']
+  end
+
+  def setting_default_sensitive
+    settings['default_sensitive']
+  end
+
+  def setting_unfollow_modal
+    settings['web.unfollow_modal']
+  end
+
+  def setting_boost_modal
+    settings['web.reblog_modal']
+  end
+
+  def setting_delete_modal
+    settings['web.delete_modal']
+  end
+
+  def setting_reduce_motion
+    settings['web.reduce_motion']
+  end
+
+  def setting_system_font_ui
+    settings['web.use_system_font']
+  end
+
+  def setting_noindex
+    settings['noindex']
+  end
+
+  def setting_theme
+    settings['theme']
+  end
+
+  def setting_display_media
+    settings['web.display_media']
+  end
+
+  def setting_expand_spoilers
+    settings['web.expand_content_warnings']
+  end
+
+  def setting_default_language
+    settings['default_language']
+  end
+
+  def setting_aggregate_reblogs
+    settings['aggregate_reblogs']
+  end
+
+  def setting_show_application
+    settings['show_application']
+  end
+
+  def setting_advanced_layout
+    settings['web.advanced_layout']
+  end
+
+  def setting_use_blurhash
+    settings['web.use_blurhash']
+  end
+
+  def setting_use_pending_items
+    settings['web.use_pending_items']
+  end
+
+  def setting_trends
+    settings['web.trends']
+  end
+
+  def setting_crop_images
+    settings['web.crop_images']
+  end
+
+  def setting_disable_swiping
+    settings['web.disable_swiping']
+  end
+
+  def setting_always_send_emails
+    settings['always_send_emails']
+  end
+
+  def setting_default_privacy
+    settings['default_privacy'] || (account.locked? ? 'private' : 'public')
+  end
+
+  def allows_report_emails?
+    settings['notification_emails.report']
+  end
+
+  def allows_pending_account_emails?
+    settings['notification_emails.pending_account']
+  end
+
+  def allows_appeal_emails?
+    settings['notification_emails.appeal']
+  end
+
+  def allows_trends_review_emails?
+    settings['notification_emails.trends']
+  end
+
+  def aggregates_reblogs?
+    settings['aggregate_reblogs']
+  end
+
+  def shows_application?
+    settings['show_application']
+  end
+
+  def show_all_media?
+    settings['web.display_media'] == 'show_all'
+  end
+
+  def hide_all_media?
+    settings['web.display_media'] == 'hide_all'
+  end
+end
diff --git a/app/models/custom_filter.rb b/app/models/custom_filter.rb
index d85e196e9..0f4fd78cb 100644
--- a/app/models/custom_filter.rb
+++ b/app/models/custom_filter.rb
@@ -15,7 +15,7 @@
 #
 
 class CustomFilter < ApplicationRecord
-  self.ignored_columns = %w(whole_word irreversible)
+  self.ignored_columns += %w(whole_word irreversible)
 
   alias_attribute :title, :phrase
   alias_attribute :filter_action, :action
diff --git a/app/models/email_domain_block.rb b/app/models/email_domain_block.rb
index 276e7d31a..3c9be51ca 100644
--- a/app/models/email_domain_block.rb
+++ b/app/models/email_domain_block.rb
@@ -12,7 +12,7 @@
 #
 
 class EmailDomainBlock < ApplicationRecord
-  self.ignored_columns = %w(
+  self.ignored_columns += %w(
     ips
     last_refresh_at
   )
diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb
index 62c9828c6..0367b4af7 100644
--- a/app/models/media_attachment.rb
+++ b/app/models/media_attachment.rb
@@ -39,11 +39,11 @@ class MediaAttachment < ApplicationRecord
 
   MAX_DESCRIPTION_LENGTH = 1_500
 
-  IMAGE_LIMIT = (ENV['MAX_IMAGE_SIZE'] || 10.megabytes).to_i
-  VIDEO_LIMIT = (ENV['MAX_VIDEO_SIZE'] || 40.megabytes).to_i
+  IMAGE_LIMIT = (ENV['MAX_IMAGE_SIZE'] || 16.megabytes).to_i
+  VIDEO_LIMIT = (ENV['MAX_VIDEO_SIZE'] || 99.megabytes).to_i
 
-  MAX_VIDEO_MATRIX_LIMIT = 2_304_000 # 1920x1200px
-  MAX_VIDEO_FRAME_RATE   = 60
+  MAX_VIDEO_MATRIX_LIMIT = 8_294_400 # 3840x2160px
+  MAX_VIDEO_FRAME_RATE   = 120
 
   IMAGE_FILE_EXTENSIONS = %w(.jpg .jpeg .png .gif .webp .heic .heif .avif).freeze
   VIDEO_FILE_EXTENSIONS = %w(.webm .mp4 .m4v .mov).freeze
@@ -69,7 +69,7 @@ class MediaAttachment < ApplicationRecord
 
   IMAGE_STYLES = {
     original: {
-      pixels: 2_073_600, # 1920x1080px
+      pixels: 8_294_400, # 3840x2160px
       file_geometry_parser: FastGeometryParser,
     }.freeze,
 
diff --git a/app/models/preview_card.rb b/app/models/preview_card.rb
index 6bce16562..a738940be 100644
--- a/app/models/preview_card.rb
+++ b/app/models/preview_card.rb
@@ -36,7 +36,7 @@ class PreviewCard < ApplicationRecord
   include Attachmentable
 
   IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze
-  LIMIT = 1.megabytes
+  LIMIT = 2.megabytes
 
   BLURHASH_OPTIONS = {
     x_comp: 4,
@@ -121,7 +121,7 @@ class PreviewCard < ApplicationRecord
     def image_styles(file)
       styles = {
         original: {
-          geometry: '400x400>',
+          pixels: 230_400, # 640x360px
           file_geometry_parser: FastGeometryParser,
           convert_options: '-coalesce',
           blurhash: BLURHASH_OPTIONS,
diff --git a/app/models/report.rb b/app/models/report.rb
index a9940459d..c3a0c4c8b 100644
--- a/app/models/report.rb
+++ b/app/models/report.rb
@@ -21,7 +21,7 @@
 #
 
 class Report < ApplicationRecord
-  self.ignored_columns = %w(action_taken)
+  self.ignored_columns += %w(action_taken)
 
   include Paginable
   include RateLimitable
diff --git a/app/models/status_edit.rb b/app/models/status_edit.rb
index 653a04252..fa35e38ac 100644
--- a/app/models/status_edit.rb
+++ b/app/models/status_edit.rb
@@ -21,7 +21,7 @@
 class StatusEdit < ApplicationRecord
   include RateLimitable
 
-  self.ignored_columns = %w(
+  self.ignored_columns += %w(
     media_attachments_changed
   )
 
diff --git a/app/models/user.rb b/app/models/user.rb
index 744d66c8f..3471bb2c1 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -39,10 +39,11 @@
 #  webauthn_id               :string
 #  sign_up_ip                :inet
 #  role_id                   :bigint(8)
+#  settings                  :text
 #
 
 class User < ApplicationRecord
-  self.ignored_columns = %w(
+  self.ignored_columns += %w(
     remember_created_at
     remember_token
     current_sign_in_ip
@@ -51,9 +52,9 @@ class User < ApplicationRecord
     filtered_languages
   )
 
-  include Settings::Extend
   include Redisable
   include LanguagesHelper
+  include HasUserSettings
 
   # The home and list feeds will be stored in Redis for this amount
   # of time, and status fan-out to followers will include only people
@@ -132,13 +133,6 @@ class User < ApplicationRecord
 
   has_many :session_activations, dependent: :destroy
 
-  delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :favourite_modal, :delete_modal,
-           :reduce_motion, :system_font_ui, :noindex, :flavour, :skin, :display_media, :hide_followers_count,
-           :expand_spoilers, :default_language, :aggregate_reblogs, :show_application,
-           :advanced_layout, :use_blurhash, :use_pending_items, :trends, :crop_images,
-           :disable_swiping, :always_send_emails, :default_content_type, :system_emoji_font,
-           to: :settings, prefix: :setting, allow_nil: false
-
   delegate :can?, to: :role
 
   attr_reader :invite_code
@@ -303,50 +297,6 @@ class User < ApplicationRecord
     save!
   end
 
-  def prefers_noindex?
-    setting_noindex
-  end
-
-  def preferred_posting_language
-    valid_locale_cascade(settings.default_language, locale, I18n.locale)
-  end
-
-  def setting_default_privacy
-    settings.default_privacy || (account.locked? ? 'private' : 'public')
-  end
-
-  def allows_report_emails?
-    settings.notification_emails['report']
-  end
-
-  def allows_pending_account_emails?
-    settings.notification_emails['pending_account']
-  end
-
-  def allows_appeal_emails?
-    settings.notification_emails['appeal']
-  end
-
-  def allows_trending_tags_review_emails?
-    settings.notification_emails['trending_tag']
-  end
-
-  def allows_trending_links_review_emails?
-    settings.notification_emails['trending_link']
-  end
-
-  def allows_trending_statuses_review_emails?
-    settings.notification_emails['trending_status']
-  end
-
-  def aggregates_reblogs?
-    @aggregates_reblogs ||= settings.aggregate_reblogs
-  end
-
-  def shows_application?
-    @shows_application ||= settings.show_application
-  end
-
   def token_for_app(app)
     return nil if app.nil? || app.owner != self
 
@@ -426,14 +376,6 @@ class User < ApplicationRecord
     send_reset_password_instructions
   end
 
-  def show_all_media?
-    setting_display_media == 'show_all'
-  end
-
-  def hide_all_media?
-    setting_display_media == 'hide_all'
-  end
-
   protected
 
   def send_devise_notification(notification, *args, **kwargs)
@@ -503,7 +445,8 @@ class User < ApplicationRecord
   def sanitize_languages
     return if chosen_languages.nil?
 
-    chosen_languages.reject!(&:blank?)
+    chosen_languages.compact_blank!
+
     self.chosen_languages = nil if chosen_languages.empty?
   end
 
diff --git a/app/models/user_settings.rb b/app/models/user_settings.rb
new file mode 100644
index 000000000..2c025d6c5
--- /dev/null
+++ b/app/models/user_settings.rb
@@ -0,0 +1,99 @@
+# frozen_string_literal: true
+
+class UserSettings
+  class Error < StandardError; end
+  class KeyError < Error; end
+
+  include UserSettings::DSL
+  include UserSettings::Glue
+
+  setting :always_send_emails, default: false
+  setting :aggregate_reblogs, default: true
+  setting :theme, default: -> { ::Setting.theme }
+  setting :noindex, default: -> { ::Setting.noindex }
+  setting :show_application, default: true
+  setting :default_language, default: nil
+  setting :default_sensitive, default: false
+  setting :default_privacy, default: nil
+
+  namespace :web do
+    setting :crop_images, default: true
+    setting :advanced_layout, default: false
+    setting :trends, default: true
+    setting :use_blurhash, default: true
+    setting :use_pending_items, default: false
+    setting :use_system_font, default: false
+    setting :disable_swiping, default: false
+    setting :delete_modal, default: true
+    setting :reblog_modal, default: false
+    setting :unfollow_modal, default: true
+    setting :reduce_motion, default: false
+    setting :expand_content_warnings, default: false
+    setting :display_media, default: 'default', in: %w(default show_all hide_all)
+    setting :auto_play, default: false
+  end
+
+  namespace :notification_emails do
+    setting :follow, default: true
+    setting :reblog, default: false
+    setting :favourite, default: false
+    setting :mention, default: true
+    setting :follow_request, default: true
+    setting :report, default: true
+    setting :pending_account, default: true
+    setting :trends, default: true
+    setting :appeal, default: true
+  end
+
+  namespace :interactions do
+    setting :must_be_follower, default: false
+    setting :must_be_following, default: false
+    setting :must_be_following_dm, default: false
+  end
+
+  def initialize(original_hash)
+    @original_hash = original_hash || {}
+  end
+
+  def [](key)
+    key = key.to_sym
+
+    raise KeyError, "Undefined setting: #{key}" unless self.class.definition_for?(key)
+
+    if @original_hash.key?(key)
+      @original_hash[key]
+    else
+      self.class.definition_for(key).default_value
+    end
+  end
+
+  def []=(key, value)
+    key = key.to_sym
+
+    raise KeyError, "Undefined setting: #{key}" unless self.class.definition_for?(key)
+
+    typecast_value = self.class.definition_for(key).type_cast(value)
+
+    if typecast_value.nil?
+      @original_hash.delete(key)
+    else
+      @original_hash[key] = typecast_value
+    end
+  end
+
+  def update(params)
+    params.each do |k, v|
+      self[k] = v unless v.nil?
+    end
+  end
+
+  keys.each do |key|
+    define_method(key) do
+      self[key]
+    end
+  end
+
+  def as_json
+    @original_hash
+  end
+end
diff --git a/app/models/user_settings/dsl.rb b/app/models/user_settings/dsl.rb
new file mode 100644
index 000000000..26238bbbe
--- /dev/null
+++ b/app/models/user_settings/dsl.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module UserSettings::DSL
+  module ClassMethods
+    def setting(key, options = {})
+      @definitions ||= {}
+
+      UserSettings::Setting.new(key, options).tap do |s|
+        @definitions[s.key] = s
+      end
+    end
+
+    def namespace(key, &block)
+      @definitions ||= {}
+
+      UserSettings::Namespace.new(key).configure(&block).tap do |n|
+        @definitions.merge!(n.definitions)
+      end
+    end
+
+    def keys
+      @definitions.keys
+    end
+
+    def definition_for(key)
+      @definitions[key.to_sym]
+    end
+
+    def definition_for?(key)
+      @definitions.key?(key.to_sym)
+    end
+  end
+
+  def self.included(base)
+    base.extend ClassMethods
+  end
+end
diff --git a/app/models/user_settings/glue.rb b/app/models/user_settings/glue.rb
new file mode 100644
index 000000000..02066a411
--- /dev/null
+++ b/app/models/user_settings/glue.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module UserSettings::Glue
+  def to_model
+    self
+  end
+
+  def to_key
+    ''
+  end
+
+  def persisted?
+    false
+  end
+
+  def type_for_attribute(key)
+    self.class.definition_for(key)&.type
+  end
+
+  def has_attribute?(key) # rubocop:disable Naming/PredicateName
+    self.class.definition_for?(key)
+  end
+end
diff --git a/app/models/user_settings/namespace.rb b/app/models/user_settings/namespace.rb
new file mode 100644
index 000000000..b8f7e092e
--- /dev/null
+++ b/app/models/user_settings/namespace.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class UserSettings::Namespace
+  attr_reader :name, :definitions
+
+  def initialize(name)
+    @name        = name.to_sym
+    @definitions = {}
+  end
+
+  def configure(&block)
+    instance_eval(&block)
+    self
+  end
+
+  def setting(key, options = {})
+    UserSettings::Setting.new(key, options.merge(namespace: name)).tap do |s|
+      @definitions[s.key] = s
+    end
+  end
+end
diff --git a/app/models/user_settings/setting.rb b/app/models/user_settings/setting.rb
new file mode 100644
index 000000000..5f5504254
--- /dev/null
+++ b/app/models/user_settings/setting.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+class UserSettings::Setting
+  attr_reader :name, :namespace, :in
+
+  def initialize(name, options = {})
+    @name          = name.to_sym
+    @default_value = options[:default]
+    @namespace     = options[:namespace]
+    @in            = options[:in]
+  end
+
+  def default_value
+    if @default_value.respond_to?(:call)
+      @default_value.call
+    else
+      @default_value
+    end
+  end
+
+  def type
+    case default_value
+    when TrueClass, FalseClass
+      ActiveModel::Type::Boolean.new
+    else
+      ActiveModel::Type::String.new
+    end
+  end
+
+  def type_cast(value)
+    if type.respond_to?(:cast)
+      type.cast(value)
+    else
+      value
+    end
+  end
+
+  def to_a
+    [key, default_value]
+  end
+
+  def key
+    if namespace
+      "#{namespace}.#{name}".to_sym
+    else
+      name
+    end
+  end
+end
diff --git a/app/serializers/rest/admin/webhook_event_serializer.rb b/app/serializers/rest/admin/webhook_event_serializer.rb
index fe0ac23f9..b6d2616e5 100644
--- a/app/serializers/rest/admin/webhook_event_serializer.rb
+++ b/app/serializers/rest/admin/webhook_event_serializer.rb
@@ -7,6 +7,8 @@ class REST::Admin::WebhookEventSerializer < ActiveModel::Serializer
       REST::Admin::AccountSerializer
     when 'Report'
       REST::Admin::ReportSerializer
+    when 'Status'
+      REST::StatusSerializer
     else
       super
     end
diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb
index 4c7acbcac..994ca588a 100644
--- a/app/services/notify_service.rb
+++ b/app/services/notify_service.rb
@@ -3,6 +3,11 @@
 class NotifyService < BaseService
   include Redisable
 
+  NON_EMAIL_TYPES = %i(
+    admin.report
+    admin.sign_up
+  ).freeze
+
   def call(recipient, type, activity)
     @recipient    = recipient
     @activity     = activity
@@ -36,11 +41,11 @@ class NotifyService < BaseService
   end
 
   def optional_non_follower?
-    @recipient.user.settings.interactions['must_be_follower']  && !@notification.from_account.following?(@recipient)
+    @recipient.user.settings['interactions.must_be_follower']  && !@notification.from_account.following?(@recipient)
   end
 
   def optional_non_following?
-    @recipient.user.settings.interactions['must_be_following'] && !following_sender?
+    @recipient.user.settings['interactions.must_be_following'] && !following_sender?
   end
 
   def message?
@@ -82,7 +87,7 @@ class NotifyService < BaseService
 
   def optional_non_following_and_direct?
     direct_message? &&
-      @recipient.user.settings.interactions['must_be_following_dm'] &&
+      @recipient.user.settings['interactions.must_be_following_dm'] &&
       !following_sender? &&
       !response_to_recipient?
   end
@@ -171,6 +176,6 @@ class NotifyService < BaseService
   end
 
   def send_email_for_notification_type?
-    @recipient.user.settings.notification_emails[@notification.type.to_s]
+    NON_EMAIL_TYPES.exclude?(@notification.type) && @recipient.user.settings["notification_emails.#{@notification.type}"]
   end
 end
diff --git a/app/services/update_status_service.rb b/app/services/update_status_service.rb
index 7a8245839..de6f1e6d1 100644
--- a/app/services/update_status_service.rb
+++ b/app/services/update_status_service.rb
@@ -143,9 +143,9 @@ class UpdateStatusService < BaseService
     poll = @status.preloadable_poll
 
     # If the poll had no expiration date set but now has, or now has a sooner
-    # expiration date, and people have voted, schedule a notification
+    # expiration date, schedule a notification
 
-    return unless poll.present? && poll.expires_at.present? && poll.votes.exists?
+    return unless poll.present? && poll.expires_at.present?
 
     PollExpirationNotifyWorker.remove_from_scheduled(poll.id) if @previous_expires_at.present? && @previous_expires_at > poll.expires_at
     PollExpirationNotifyWorker.perform_at(poll.expires_at + 5.minutes, poll.id)
diff --git a/app/views/kaminari/_gap.html.haml b/app/views/kaminari/_gap.html.haml
new file mode 100644
index 000000000..0f9cff8fe
--- /dev/null
+++ b/app/views/kaminari/_gap.html.haml
@@ -0,0 +1,9 @@
+-#
+  Non-link tag that stands for skipped pages...
+  available local variables
+    current_page:  a page object for the currently displayed page
+    total_pages:   total number of pages
+    per_page:      number of items to fetch per page
+    remote:        data-remote
+%span.page.gap
+  != t('pagination.truncate')
diff --git a/app/views/settings/login_activities/_login_activity.html.haml b/app/views/settings/login_activities/_login_activity.html.haml
index 9f4c24d87..94ed60312 100644
--- a/app/views/settings/login_activities/_login_activity.html.haml
+++ b/app/views/settings/login_activities/_login_activity.html.haml
@@ -1,6 +1,6 @@
 - method_str = content_tag(:span, login_activity.omniauth? ? t(login_activity.provider, scope: 'auth.providers') : t(login_activity.authentication_method, scope: 'login_activities.authentication_methods'), class: 'target')
 - ip_str = content_tag(:span, login_activity.ip, class: 'target')
-- browser_str = content_tag(:span, t('sessions.description', browser: t("sessions.browsers.#{login_activity.browser}", default: login_activity.browser.to_s), platform: t("sessions.platforms.#{login_activity.platform}", default: login_activity.platform.to_)), class: 'target', title: login_activity.user_agent)
+- browser_str = content_tag(:span, t('sessions.description', browser: t("sessions.browsers.#{login_activity.browser}", default: login_activity.browser.to_s), platform: t("sessions.platforms.#{login_activity.platform}", default: login_activity.platform.to_s)), class: 'target', title: login_activity.user_agent)
 
 .log-entry
   .log-entry__header
diff --git a/app/views/settings/preferences/appearance/show.html.haml b/app/views/settings/preferences/appearance/show.html.haml
index 16bb85068..dc82ce5b6 100644
--- a/app/views/settings/preferences/appearance/show.html.haml
+++ b/app/views/settings/preferences/appearance/show.html.haml
@@ -5,8 +5,9 @@
   = button_tag t('generic.save_changes'), class: 'button', form: 'edit_user'
 
 = simple_form_for current_user, url: settings_preferences_appearance_path, html: { method: :put, id: 'edit_user' } do |f|
-  .fields-group
-    = f.input :locale, collection: I18n.available_locales, wrapper: :with_label, include_blank: false, label_method: lambda { |locale| native_locale_name(locale) }, selected: I18n.locale, hint: false
+  .fields-row
+    .fields-group
+      = f.input :locale, collection: I18n.available_locales, wrapper: :with_label, include_blank: false, label_method: lambda { |locale| native_locale_name(locale) }, selected: I18n.locale, hint: false
 
   - unless I18n.locale == :en
     .flash-message.translation-prompt
@@ -14,53 +15,53 @@
       = link_to t('appearance.localization.glitch_guide_link'), target: '_blank', rel: 'noopener noreferrer' do
         = t('appearance.localization.glitch_guide_link_text')
 
-  %h4= t 'appearance.advanced_web_interface'
+  = f.simple_fields_for :settings, current_user.settings do |ff|
+    %h4= t 'appearance.advanced_web_interface'
 
-  %p.hint= t 'appearance.advanced_web_interface_hint'
+    %p.hint= t 'appearance.advanced_web_interface_hint'
 
-  .fields-group
-    = f.input :setting_advanced_layout, as: :boolean, wrapper: :with_label, hint: false
+    .fields-group
+      = ff.input :'web.advanced_layout', wrapper: :with_label, hint: false, label: I18n.t('simple_form.labels.defaults.setting_advanced_layout')
+    %h4= t 'appearance.animations_and_accessibility'
 
-  %h4= t 'appearance.animations_and_accessibility'
+    .fields-group
+      = ff.input :'web.use_pending_items', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_use_pending_items'), hint: I18n.t('simple_form.hints.defaults.setting_use_pending_items')
 
-  .fields-group
-    = f.input :setting_use_pending_items, as: :boolean, wrapper: :with_label
+    .fields-group
+      = ff.input :'web.auto_play', wrapper: :with_label, recommended: true, label: I18n.t('simple_form.labels.defaults.setting_auto_play_gif')
+      = ff.input :'web.reduce_motion', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_reduce_motion')
+      = ff.input :'web.disable_swiping', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_disable_swiping')
+      = ff.input :'web.use_system_font', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_system_font_ui')
+      = ff.input :'web.use_system_emoji_font', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_system_emoji_font'), glitch_only: true
 
-  .fields-group
-    = f.input :setting_auto_play_gif, as: :boolean, wrapper: :with_label, recommended: true
-    = f.input :setting_reduce_motion, as: :boolean, wrapper: :with_label
-    = f.input :setting_disable_swiping, as: :boolean, wrapper: :with_label
-    = f.input :setting_system_font_ui, as: :boolean, wrapper: :with_label
-    = f.input :setting_system_emoji_font, as: :boolean, wrapper: :with_label, glitch_only: true
+    %h4= t 'appearance.toot_layout'
 
-  %h4= t 'appearance.toot_layout'
+    .fields-group
+      = ff.input :'web.crop_images', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_crop_images')
 
-  .fields-group
-    = f.input :setting_crop_images, as: :boolean, wrapper: :with_label
+    %h4= t 'appearance.discovery'
 
-  %h4= t 'appearance.discovery'
+    .fields-group
+      = ff.input :'web.trends', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_trends')
 
-  .fields-group
-    = f.input :setting_trends, as: :boolean, wrapper: :with_label
+    %h4= t 'appearance.confirmation_dialogs'
 
-  %h4= t 'appearance.confirmation_dialogs'
+    .fields-group
+      = ff.input :'web.unfollow_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_unfollow_modal')
+      = ff.input :'web.reblog_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_boost_modal')
+      = ff.input :'web.favourite_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_favourite_modal'), glitch_only: true
+      = ff.input :'web.delete_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_delete_modal')
 
-  .fields-group
-    = f.input :setting_unfollow_modal, as: :boolean, wrapper: :with_label
-    = f.input :setting_boost_modal, as: :boolean, wrapper: :with_label
-    = f.input :setting_favourite_modal, as: :boolean, wrapper: :with_label, glitch_only: true
-    = f.input :setting_delete_modal, as: :boolean, wrapper: :with_label
+    %h4= t 'appearance.sensitive_content'
 
-  %h4= t 'appearance.sensitive_content'
+    .fields-group
+      = ff.input :'web.display_media', collection: ['default', 'show_all', 'hide_all'],label_method: lambda { |item| t("simple_form.hints.defaults.setting_display_media_#{item}") }, hint: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', wrapper: :with_floating_label, label: I18n.t('simple_form.labels.defaults.setting_display_media')
 
-  .fields-group
-    = f.input :setting_display_media, collection: ['default', 'show_all', 'hide_all'], label_method: lambda { |item| t("simple_form.hints.defaults.setting_display_media_#{item}") }, hint: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', wrapper: :with_floating_label
+    .fields-group
+      = ff.input :'web.use_blurhash', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_use_blurhash'), hint: I18n.t('simple_form.hints.defaults.setting_use_blurhash')
 
-  .fields-group
-    = f.input :setting_use_blurhash, as: :boolean, wrapper: :with_label
-
-  .fields-group
-    = f.input :setting_expand_spoilers, as: :boolean, wrapper: :with_label
+    .fields-group
+      = ff.input :'web.expand_content_warnings', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_expand_spoilers')
 
   .actions
     = f.button :button, t('generic.save_changes'), type: :submit
diff --git a/app/views/settings/preferences/notifications/show.html.haml b/app/views/settings/preferences/notifications/show.html.haml
index a03faa145..cfc468eef 100644
--- a/app/views/settings/preferences/notifications/show.html.haml
+++ b/app/views/settings/preferences/notifications/show.html.haml
@@ -11,27 +11,27 @@
 
   %p.hint= t 'notifications.email_events_hint'
 
-  .fields-group
-    = f.simple_fields_for :notification_emails, hash_to_object(current_user.settings.notification_emails) do |ff|
-      = ff.input :follow, as: :boolean, wrapper: :with_label
-      = ff.input :follow_request, as: :boolean, wrapper: :with_label
-      = ff.input :reblog, as: :boolean, wrapper: :with_label
-      = ff.input :favourite, as: :boolean, wrapper: :with_label
-      = ff.input :mention, as: :boolean, wrapper: :with_label
-      = ff.input :report, as: :boolean, wrapper: :with_label if current_user.can?(:manage_reports)
-      = ff.input :appeal, as: :boolean, wrapper: :with_label if current_user.can?(:manage_appeals)
-      = ff.input :pending_account, as: :boolean, wrapper: :with_label if current_user.can?(:manage_users)
-      = ff.input :trending_tag, as: :boolean, wrapper: :with_label if current_user.can?(:manage_taxonomies)
-      = ff.input :trending_link, as: :boolean, wrapper: :with_label if current_user.can?(:manage_taxonomies)
-      = ff.input :trending_status, as: :boolean, wrapper: :with_label if current_user.can?(:manage_taxonomies)
-
-  .fields-group
-    = f.input :setting_always_send_emails, as: :boolean, wrapper: :with_label
+  = f.simple_fields_for :settings, current_user.settings do |ff|
+    .fields-group
+      = ff.input :'notification_emails.follow', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.follow')
+      = ff.input :'notification_emails.follow_request', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.follow_request')
+      = ff.input :'notification_emails.reblog', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.reblog')
+      = ff.input :'notification_emails.favourite', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.favourite')
+      = ff.input :'notification_emails.mention', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.mention')
+      = ff.input :'notification_emails.report', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.report') if current_user.can?(:manage_reports)
+      = ff.input :'notification_emails.appeal', as: :boolean, wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.appeal') if current_user.can?(:manage_appeals)
+      = ff.input :'notification_emails.pending_account', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.pending_account') if current_user.can?(:manage_users)
+      = ff.input :'notification_emails.trends', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.trending_tag') if current_user.can?(:manage_taxonomies)
+      = ff.input :'notification_emails.link_trends', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.trending_link') if current_user.can?(:manage_taxonomies)
+      = ff.input :'notification_emails.status_trends', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.trending_status') if current_user.can?(:manage_taxonomies)
+
+    .fields-group
+      = ff.input :always_send_emails, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_always_send_emails'), hint: I18n.t('simple_form.hints.defaults.setting_always_send_emails')
 
   %h4= t 'notifications.other_settings'
 
   .fields-group
-    = f.simple_fields_for :interactions, hash_to_object(current_user.settings.interactions) do |ff|
-      = ff.input :must_be_follower, as: :boolean, wrapper: :with_label
-      = ff.input :must_be_following, as: :boolean, wrapper: :with_label
-      = ff.input :must_be_following_dm, as: :boolean, wrapper: :with_label
+    = f.simple_fields_for :settings, current_user.settings do |ff|
+      = ff.input :'interactions.must_be_follower', wrapper: :with_label, label: I18n.t('simple_form.labels.interactions.must_be_follower')
+      = ff.input :'interactions.must_be_following', wrapper: :with_label, label: I18n.t('simple_form.labels.interactions.must_be_following')
+      = ff.input :'interactions.must_be_following_dm', wrapper: :with_label, label: I18n.t('simple_form.labels.interactions.must_be_following_dm')
diff --git a/app/views/settings/preferences/other/show.html.haml b/app/views/settings/preferences/other/show.html.haml
index fb3d21060..ebb89f44b 100644
--- a/app/views/settings/preferences/other/show.html.haml
+++ b/app/views/settings/preferences/other/show.html.haml
@@ -7,30 +7,31 @@
 = simple_form_for current_user, url: settings_preferences_other_path, html: { method: :put, id: 'edit_preferences' } do |f|
   = render 'shared/error_messages', object: current_user
 
-  .fields-group
-    = f.input :setting_noindex, as: :boolean, wrapper: :with_label
-
-  .fields-group
-    = f.input :setting_aggregate_reblogs, as: :boolean, wrapper: :with_label, recommended: true
+  = f.simple_fields_for :settings, current_user.settings do |ff|
+    .fields-group
+      = ff.input :noindex, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_noindex'), hint: I18n.t('simple_form.hints.defaults.setting_noindex')
 
-  - unless Setting.hide_followers_count
     .fields-group
-      = f.input :setting_hide_followers_count, as: :boolean, wrapper: :with_label, glitch_only: true
+      = ff.input :aggregate_reblogs, wrapper: :with_label, recommended: true, label: I18n.t('simple_form.labels.defaults.setting_aggregate_reblogs'), hint: I18n.t('simple_form.hints.defaults.setting_aggregate_reblogs')
 
-  %h4= t 'preferences.posting_defaults'
+    - unless Setting.hide_followers_count
+      .fields-group
+        = ff.input :hide_followers_count, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_hide_followers_count'), glitch_only: true
 
-  .fields-row
-    .fields-group.fields-row__column.fields-row__column-6
-      = f.input :setting_default_privacy, collection: Status.selectable_visibilities, wrapper: :with_label, include_blank: false, label_method: lambda { |visibility| safe_join([I18n.t("statuses.visibilities.#{visibility}"), I18n.t("statuses.visibilities.#{visibility}_long")], ' - ') }, required: false, hint: false
+    %h4= t 'preferences.posting_defaults'
 
-    .fields-group.fields-row__column.fields-row__column-6
-      = f.input :setting_default_language, collection: [nil] + filterable_languages, wrapper: :with_label, label_method: lambda { |locale| locale.nil? ? I18n.t('statuses.default_language') : native_locale_name(locale) }, required: false, include_blank: false, hint: false
+    .fields-row
+      .fields-group.fields-row__column.fields-row__column-6
+        = ff.input :default_privacy, collection: Status.selectable_visibilities, wrapper: :with_label, include_blank: false, label_method: lambda { |visibility| safe_join([I18n.t("statuses.visibilities.#{visibility}"), I18n.t("statuses.visibilities.#{visibility}_long")], ' - ') }, required: false, hint: false, label: I18n.t('simple_form.labels.defaults.setting_default_privacy')
 
-  .fields-group
-    = f.input :setting_default_sensitive, as: :boolean, wrapper: :with_label
+      .fields-group.fields-row__column.fields-row__column-6
+        = ff.input :default_language, collection: [nil] + filterable_languages, wrapper: :with_label, label_method: lambda { |locale| locale.nil? ? I18n.t('statuses.default_language') : native_locale_name(locale) }, required: false, include_blank: false, hint: false, label: I18n.t('simple_form.labels.defaults.setting_default_language')
 
-  .fields-group
-    = f.input :setting_show_application, as: :boolean, wrapper: :with_label, recommended: true
+    .fields-group
+      = ff.input :default_sensitive, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_default_sensitive'), hint: I18n.t('simple_form.hints.defaults.setting_default_sensitive')
+
+    .fields-group
+      = ff.input :show_application, wrapper: :with_label, recommended: true, label: I18n.t('simple_form.labels.defaults.setting_show_application'), hint: I18n.t('simple_form.hints.defaults.setting_show_application')
 
   .fields-group
     = f.input :setting_default_content_type, collection: ['text/plain', 'text/markdown', 'text/html'], wrapper: :with_label, include_blank: false, label_method: lambda { |item| safe_join([t("simple_form.labels.defaults.setting_default_content_type_#{item.split('/')[1]}"), content_tag(:span, t("simple_form.hints.defaults.setting_default_content_type_#{item.split('/')[1]}"), class: 'hint')]) }, required: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', glitch_only: true
diff --git a/app/workers/poll_expiration_notify_worker.rb b/app/workers/poll_expiration_notify_worker.rb
index 0e29a5f60..b7a60fab8 100644
--- a/app/workers/poll_expiration_notify_worker.rb
+++ b/app/workers/poll_expiration_notify_worker.rb
@@ -3,7 +3,7 @@
 class PollExpirationNotifyWorker
   include Sidekiq::Worker
 
-  sidekiq_options lock: :until_executed
+  sidekiq_options lock: :until_executing
 
   def perform(poll_id)
     @poll = Poll.find(poll_id)
diff --git a/app/workers/scheduler/indexing_scheduler.rb b/app/workers/scheduler/indexing_scheduler.rb
index c42396629..1bbe9cd5d 100644
--- a/app/workers/scheduler/indexing_scheduler.rb
+++ b/app/workers/scheduler/indexing_scheduler.rb
@@ -6,17 +6,21 @@ class Scheduler::IndexingScheduler
 
   sidekiq_options retry: 0
 
+  IMPORT_BATCH_SIZE = 1000
+  SCAN_BATCH_SIZE = 10 * IMPORT_BATCH_SIZE
+
   def perform
     return unless Chewy.enabled?
 
     indexes.each do |type|
       with_redis do |redis|
-        ids = redis.smembers("chewy:queue:#{type.name}")
-
-        type.import!(ids)
-
-        redis.pipelined do |pipeline|
-          ids.each { |id| pipeline.srem("chewy:queue:#{type.name}", id) }
+        redis.sscan_each("chewy:queue:#{type.name}", count: SCAN_BATCH_SIZE) do |ids|
+          redis.pipelined do
+            ids.each_slice(IMPORT_BATCH_SIZE) do |slice_ids|
+              type.import!(slice_ids)
+              redis.srem("chewy:queue:#{type.name}", slice_ids)
+            end
+          end
         end
       end
     end