about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
authorStarfall <us@starfall.systems>2020-12-24 13:36:25 -0600
committerStarfall <us@starfall.systems>2020-12-24 13:36:25 -0600
commit6ed4e874c5ace36344f77b3f096c4089d9b11e01 (patch)
tree83b2675d297f56a75b5e5dec33c644bc19f6cf1b /app
parentab127fd7941b7c84e6d6fe3071d41f52affb143c (diff)
parent225c934a1b66e2fcbedbda7936666c1ca3c9a04b (diff)
Merge branch 'glitch' into main
Diffstat (limited to 'app')
-rw-r--r--app/controllers/admin/domain_blocks_controller.rb9
-rw-r--r--app/controllers/admin/instances_controller.rb44
-rw-r--r--app/controllers/api/base_controller.rb2
-rw-r--r--app/controllers/api/v1/instances/peers_controller.rb2
-rw-r--r--app/controllers/application_controller.rb2
-rw-r--r--app/helpers/statuses_helper.rb20
-rw-r--r--app/javascript/core/admin.js28
-rw-r--r--app/javascript/flavours/glitch/actions/notifications.js9
-rw-r--r--app/javascript/flavours/glitch/components/autosuggest_input.js8
-rw-r--r--app/javascript/flavours/glitch/components/autosuggest_textarea.js8
-rw-r--r--app/javascript/flavours/glitch/components/status_content.js9
-rw-r--r--app/javascript/flavours/glitch/features/account_gallery/index.js2
-rw-r--r--app/javascript/flavours/glitch/features/account_timeline/index.js2
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/reply_indicator.js4
-rw-r--r--app/javascript/flavours/glitch/features/notifications/components/column_settings.js25
-rw-r--r--app/javascript/flavours/glitch/features/notifications/components/grant_permission_button.js19
-rw-r--r--app/javascript/flavours/glitch/features/notifications/components/notifications_permission_banner.js5
-rw-r--r--app/javascript/flavours/glitch/features/notifications/index.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/notifications.js3
-rw-r--r--app/javascript/flavours/glitch/reducers/settings.js2
-rw-r--r--app/javascript/flavours/glitch/styles/about.scss1
-rw-r--r--app/javascript/flavours/glitch/styles/components/columns.scss8
-rw-r--r--app/javascript/flavours/glitch/styles/components/status.scss2
-rw-r--r--app/javascript/flavours/glitch/styles/forms.scss15
-rw-r--r--app/javascript/flavours/glitch/util/rtl.js32
-rw-r--r--app/javascript/mastodon/actions/notifications.js9
-rw-r--r--app/javascript/mastodon/components/autosuggest_input.js8
-rw-r--r--app/javascript/mastodon/components/autosuggest_textarea.js8
-rw-r--r--app/javascript/mastodon/components/status_content.js18
-rw-r--r--app/javascript/mastodon/features/account_gallery/index.js10
-rw-r--r--app/javascript/mastodon/features/account_timeline/index.js4
-rw-r--r--app/javascript/mastodon/features/compose/components/reply_indicator.js6
-rw-r--r--app/javascript/mastodon/features/notifications/components/column_settings.js25
-rw-r--r--app/javascript/mastodon/features/notifications/components/grant_permission_button.js19
-rw-r--r--app/javascript/mastodon/features/notifications/components/notifications_permission_banner.js5
-rw-r--r--app/javascript/mastodon/features/notifications/index.js2
-rw-r--r--app/javascript/mastodon/locales/ar.json10
-rw-r--r--app/javascript/mastodon/locales/ast.json10
-rw-r--r--app/javascript/mastodon/locales/bg.json10
-rw-r--r--app/javascript/mastodon/locales/bn.json10
-rw-r--r--app/javascript/mastodon/locales/br.json10
-rw-r--r--app/javascript/mastodon/locales/ca.json10
-rw-r--r--app/javascript/mastodon/locales/co.json12
-rw-r--r--app/javascript/mastodon/locales/cs.json16
-rw-r--r--app/javascript/mastodon/locales/cy.json10
-rw-r--r--app/javascript/mastodon/locales/da.json10
-rw-r--r--app/javascript/mastodon/locales/de.json12
-rw-r--r--app/javascript/mastodon/locales/defaultMessages.json53
-rw-r--r--app/javascript/mastodon/locales/el.json10
-rw-r--r--app/javascript/mastodon/locales/en.json10
-rw-r--r--app/javascript/mastodon/locales/eo.json38
-rw-r--r--app/javascript/mastodon/locales/es-AR.json10
-rw-r--r--app/javascript/mastodon/locales/es.json12
-rw-r--r--app/javascript/mastodon/locales/et.json10
-rw-r--r--app/javascript/mastodon/locales/eu.json10
-rw-r--r--app/javascript/mastodon/locales/fa.json18
-rw-r--r--app/javascript/mastodon/locales/fi.json10
-rw-r--r--app/javascript/mastodon/locales/fr.json16
-rw-r--r--app/javascript/mastodon/locales/ga.json10
-rw-r--r--app/javascript/mastodon/locales/gl.json12
-rw-r--r--app/javascript/mastodon/locales/he.json10
-rw-r--r--app/javascript/mastodon/locales/hi.json10
-rw-r--r--app/javascript/mastodon/locales/hr.json22
-rw-r--r--app/javascript/mastodon/locales/hu.json10
-rw-r--r--app/javascript/mastodon/locales/hy.json16
-rw-r--r--app/javascript/mastodon/locales/id.json10
-rw-r--r--app/javascript/mastodon/locales/io.json10
-rw-r--r--app/javascript/mastodon/locales/is.json10
-rw-r--r--app/javascript/mastodon/locales/it.json10
-rw-r--r--app/javascript/mastodon/locales/ja.json14
-rw-r--r--app/javascript/mastodon/locales/ka.json10
-rw-r--r--app/javascript/mastodon/locales/kab.json12
-rw-r--r--app/javascript/mastodon/locales/kk.json10
-rw-r--r--app/javascript/mastodon/locales/kn.json10
-rw-r--r--app/javascript/mastodon/locales/ko.json20
-rw-r--r--app/javascript/mastodon/locales/ku.json16
-rw-r--r--app/javascript/mastodon/locales/lt.json10
-rw-r--r--app/javascript/mastodon/locales/lv.json10
-rw-r--r--app/javascript/mastodon/locales/mk.json10
-rw-r--r--app/javascript/mastodon/locales/ml.json268
-rw-r--r--app/javascript/mastodon/locales/mr.json10
-rw-r--r--app/javascript/mastodon/locales/ms.json10
-rw-r--r--app/javascript/mastodon/locales/nl.json54
-rw-r--r--app/javascript/mastodon/locales/nn.json10
-rw-r--r--app/javascript/mastodon/locales/no.json10
-rw-r--r--app/javascript/mastodon/locales/oc.json10
-rw-r--r--app/javascript/mastodon/locales/pl.json12
-rw-r--r--app/javascript/mastodon/locales/pt-BR.json10
-rw-r--r--app/javascript/mastodon/locales/pt-PT.json10
-rw-r--r--app/javascript/mastodon/locales/ro.json10
-rw-r--r--app/javascript/mastodon/locales/ru.json10
-rw-r--r--app/javascript/mastodon/locales/sa.json10
-rw-r--r--app/javascript/mastodon/locales/sc.json184
-rw-r--r--app/javascript/mastodon/locales/sk.json10
-rw-r--r--app/javascript/mastodon/locales/sl.json10
-rw-r--r--app/javascript/mastodon/locales/sq.json10
-rw-r--r--app/javascript/mastodon/locales/sr-Latn.json10
-rw-r--r--app/javascript/mastodon/locales/sr.json10
-rw-r--r--app/javascript/mastodon/locales/sv.json20
-rw-r--r--app/javascript/mastodon/locales/szl.json10
-rw-r--r--app/javascript/mastodon/locales/ta.json10
-rw-r--r--app/javascript/mastodon/locales/tai.json10
-rw-r--r--app/javascript/mastodon/locales/te.json10
-rw-r--r--app/javascript/mastodon/locales/th.json18
-rw-r--r--app/javascript/mastodon/locales/tr.json10
-rw-r--r--app/javascript/mastodon/locales/tt.json486
-rw-r--r--app/javascript/mastodon/locales/ug.json10
-rw-r--r--app/javascript/mastodon/locales/uk.json10
-rw-r--r--app/javascript/mastodon/locales/ur.json10
-rw-r--r--app/javascript/mastodon/locales/vi.json94
-rw-r--r--app/javascript/mastodon/locales/whitelist_tt.json2
-rw-r--r--app/javascript/mastodon/locales/zgh.json180
-rw-r--r--app/javascript/mastodon/locales/zh-CN.json20
-rw-r--r--app/javascript/mastodon/locales/zh-HK.json232
-rw-r--r--app/javascript/mastodon/locales/zh-TW.json238
-rw-r--r--app/javascript/mastodon/reducers/notifications.js3
-rw-r--r--app/javascript/mastodon/reducers/settings.js2
-rw-r--r--app/javascript/mastodon/rtl.js32
-rw-r--r--app/javascript/styles/mailer.scss12
-rw-r--r--app/javascript/styles/mastodon-light/diff.scss42
-rw-r--r--app/javascript/styles/mastodon/about.scss1
-rw-r--r--app/javascript/styles/mastodon/components.scss10
-rw-r--r--app/javascript/styles/mastodon/forms.scss15
-rw-r--r--app/javascript/styles/mastodon/rtl.scss46
-rw-r--r--app/lib/activitypub/activity/create.rb4
-rw-r--r--app/lib/feed_manager.rb30
-rw-r--r--app/models/account.rb22
-rw-r--r--app/models/account_filter.rb2
-rw-r--r--app/models/concerns/domain_materializable.rb13
-rw-r--r--app/models/domain_allow.rb1
-rw-r--r--app/models/domain_block.rb21
-rw-r--r--app/models/favourite.rb2
-rw-r--r--app/models/follow.rb2
-rw-r--r--app/models/follow_request.rb2
-rw-r--r--app/models/form/admin_settings.rb2
-rw-r--r--app/models/import.rb1
-rw-r--r--app/models/instance.rb63
-rw-r--r--app/models/instance_filter.rb31
-rw-r--r--app/models/list.rb13
-rw-r--r--app/models/poll.rb2
-rw-r--r--app/models/report.rb1
-rw-r--r--app/models/status.rb12
-rw-r--r--app/models/unavailable_domain.rb2
-rw-r--r--app/models/user.rb13
-rw-r--r--app/policies/account_policy.rb4
-rw-r--r--app/policies/domain_block_policy.rb4
-rw-r--r--app/presenters/instance_presenter.rb2
-rw-r--r--app/serializers/manifest_serializer.rb40
-rw-r--r--app/services/activitypub/fetch_remote_account_service.rb2
-rw-r--r--app/services/activitypub/process_account_service.rb28
-rw-r--r--app/services/batched_remove_status_service.rb98
-rw-r--r--app/services/delete_account_service.rb150
-rw-r--r--app/services/import_service.rb7
-rw-r--r--app/services/remove_status_service.rb2
-rw-r--r--app/services/report_service.rb3
-rw-r--r--app/services/resolve_account_service.rb17
-rw-r--r--app/services/resolve_url_service.rb6
-rw-r--r--app/validators/import_validator.rb44
-rw-r--r--app/views/about/_domain_blocks.html.haml2
-rw-r--r--app/views/about/_registration.html.haml2
-rw-r--r--app/views/about/more.html.haml4
-rw-r--r--app/views/admin/accounts/show.html.haml14
-rw-r--r--app/views/admin/domain_blocks/edit.html.haml3
-rw-r--r--app/views/admin/domain_blocks/new.html.haml3
-rw-r--r--app/views/admin/instances/_instance.html.haml25
-rw-r--r--app/views/admin/instances/index.html.haml36
-rw-r--r--app/views/admin/instances/show.html.haml50
-rw-r--r--app/views/admin/reports/index.html.haml4
-rw-r--r--app/views/admin/reports/show.html.haml10
-rw-r--r--app/views/admin/settings/edit.html.haml9
-rw-r--r--app/views/auth/registrations/new.html.haml2
-rw-r--r--app/views/notification_mailer/_status.html.haml4
-rw-r--r--app/views/statuses/_detailed_status.html.haml2
-rw-r--r--app/views/statuses/_simple_status.html.haml2
-rw-r--r--app/workers/account_deletion_worker.rb2
-rw-r--r--app/workers/account_merging_worker.rb18
-rw-r--r--app/workers/scheduler/feed_cleanup_scheduler.rb32
-rw-r--r--app/workers/scheduler/instance_refresh_scheduler.rb11
178 files changed, 2521 insertions, 1507 deletions
diff --git a/app/controllers/admin/domain_blocks_controller.rb b/app/controllers/admin/domain_blocks_controller.rb
index 74a36b79c..ba927b04a 100644
--- a/app/controllers/admin/domain_blocks_controller.rb
+++ b/app/controllers/admin/domain_blocks_controller.rb
@@ -29,6 +29,7 @@ module Admin
           @domain_block = existing_domain_block
           @domain_block.update(resource_params)
         end
+
         if @domain_block.save
           DomainBlockWorker.perform_async(@domain_block.id)
           log_action :create, @domain_block
@@ -40,7 +41,7 @@ module Admin
     end
 
     def update
-      authorize :domain_block, :create?
+      authorize :domain_block, :update?
 
       @domain_block.update(update_params)
 
@@ -48,7 +49,7 @@ module Admin
 
       if @domain_block.save
         DomainBlockWorker.perform_async(@domain_block.id, severity_changed)
-        log_action :create, @domain_block
+        log_action :update, @domain_block
         redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg')
       else
         render :edit
@@ -73,11 +74,11 @@ module Admin
     end
 
     def update_params
-      params.require(:domain_block).permit(:severity, :reject_media, :reject_reports, :private_comment, :public_comment)
+      params.require(:domain_block).permit(:severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate)
     end
 
     def resource_params
-      params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment)
+      params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate)
     end
   end
 end
diff --git a/app/controllers/admin/instances_controller.rb b/app/controllers/admin/instances_controller.rb
index 1790becbf..b5918d231 100644
--- a/app/controllers/admin/instances_controller.rb
+++ b/app/controllers/admin/instances_controller.rb
@@ -2,65 +2,31 @@
 
 module Admin
   class InstancesController < BaseController
-    before_action :set_domain_block, only: :show
-    before_action :set_domain_allow, only: :show
+    before_action :set_instances, only: :index
     before_action :set_instance, only: :show
 
     def index
       authorize :instance, :index?
-
-      @instances = ordered_instances
     end
 
     def show
       authorize :instance, :show?
-
-      @following_count = Follow.where(account: Account.where(domain: params[:id])).count
-      @followers_count = Follow.where(target_account: Account.where(domain: params[:id])).count
-      @reports_count   = Report.where(target_account: Account.where(domain: params[:id])).count
-      @blocks_count    = Block.where(target_account: Account.where(domain: params[:id])).count
-      @available       = DeliveryFailureTracker.available?(params[:id])
-      @media_storage   = MediaAttachment.where(account: Account.where(domain: params[:id])).sum(:file_file_size)
-      @private_comment = @domain_block&.private_comment
-      @public_comment  = @domain_block&.public_comment
     end
 
     private
 
-    def set_domain_block
-      @domain_block = DomainBlock.rule_for(params[:id])
-    end
-
-    def set_domain_allow
-      @domain_allow = DomainAllow.rule_for(params[:id])
-    end
-
     def set_instance
-      resource   = Account.by_domain_accounts.find_by(domain: params[:id])
-      resource ||= @domain_block
-      resource ||= @domain_allow
+      @instance = Instance.find(params[:id])
+    end
 
-      if resource
-        @instance = Instance.new(resource)
-      else
-        not_found
-      end
+    def set_instances
+      @instances = filtered_instances.page(params[:page])
     end
 
     def filtered_instances
       InstanceFilter.new(whitelist_mode? ? { allowed: true } : filter_params).results
     end
 
-    def paginated_instances
-      filtered_instances.page(params[:page])
-    end
-
-    helper_method :paginated_instances
-
-    def ordered_instances
-      paginated_instances.map { |resource| Instance.new(resource) }
-    end
-
     def filter_params
       params.slice(*InstanceFilter::KEYS).permit(*InstanceFilter::KEYS)
     end
diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb
index fe199e689..85f4cc768 100644
--- a/app/controllers/api/base_controller.rb
+++ b/app/controllers/api/base_controller.rb
@@ -40,7 +40,7 @@ class Api::BaseController < ApplicationController
     render json: { error: 'This action is not allowed' }, status: 403
   end
 
-  rescue_from Mastodon::RaceConditionError do
+  rescue_from Mastodon::RaceConditionError, Seahorse::Client::NetworkingError, Stoplight::Error::RedLight do
     render json: { error: 'There was a temporary problem serving your request, please try again' }, status: 503
   end
 
diff --git a/app/controllers/api/v1/instances/peers_controller.rb b/app/controllers/api/v1/instances/peers_controller.rb
index 9fa440935..2877fec52 100644
--- a/app/controllers/api/v1/instances/peers_controller.rb
+++ b/app/controllers/api/v1/instances/peers_controller.rb
@@ -8,7 +8,7 @@ class Api::V1::Instances::PeersController < Api::BaseController
 
   def index
     expires_in 1.day, public: true
-    render_with_cache(expires_in: 1.day) { Account.remote.domains }
+    render_with_cache(expires_in: 1.day) { Instance.where.not(domain: DomainBlock.select(:domain)).pluck(:domain) }
   end
 
   private
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index e996c2217..41fe9d88a 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -29,7 +29,7 @@ class ApplicationController < ActionController::Base
   rescue_from ActiveRecord::RecordNotFound, with: :not_found
   rescue_from Mastodon::NotPermittedError, with: :forbidden
   rescue_from HTTP::Error, OpenSSL::SSL::SSLError, with: :internal_server_error
-  rescue_from Mastodon::RaceConditionError, with: :service_unavailable
+  rescue_from Mastodon::RaceConditionError, Seahorse::Client::NetworkingError, Stoplight::Error::RedLight, with: :service_unavailable
   rescue_from Mastodon::RateLimitExceededError, with: :too_many_requests
 
   before_action :store_current_location, except: :raise_not_found, unless: :devise_controller?
diff --git a/app/helpers/statuses_helper.rb b/app/helpers/statuses_helper.rb
index daed9048f..1f654f34f 100644
--- a/app/helpers/statuses_helper.rb
+++ b/app/helpers/statuses_helper.rb
@@ -92,22 +92,6 @@ module StatusesHelper
     end
   end
 
-  def rtl_status?(status)
-    status.local? ? rtl?(status.text) : rtl?(strip_tags(status.text))
-  end
-
-  def rtl?(text)
-    text = simplified_text(text)
-    rtl_words = text.scan(/[\p{Hebrew}\p{Arabic}\p{Syriac}\p{Thaana}\p{Nko}]+/m)
-
-    if rtl_words.present?
-      total_size = text.size.to_f
-      rtl_size(rtl_words) / total_size > 0.3
-    else
-      false
-    end
-  end
-
   def fa_visibility_icon(status)
     case status.visibility
     when 'public'
@@ -143,10 +127,6 @@ module StatusesHelper
     end
   end
 
-  def rtl_size(words)
-    words.reduce(0) { |acc, elem| acc + elem.size }.to_f
-  end
-
   def embedded_view?
     params[:controller] == EMBEDDED_CONTROLLER && params[:action] == EMBEDDED_ACTION
   end
diff --git a/app/javascript/core/admin.js b/app/javascript/core/admin.js
index bbc7cfac7..d2db89ca7 100644
--- a/app/javascript/core/admin.js
+++ b/app/javascript/core/admin.js
@@ -59,18 +59,46 @@ const onEnableBootstrapTimelineAccountsChange = (target) => {
     bootstrapTimelineAccountsField.disabled = !target.checked;
     if (target.checked) {
       bootstrapTimelineAccountsField.parentElement.classList.remove('disabled');
+      bootstrapTimelineAccountsField.parentElement.parentElement.classList.remove('disabled');
     } else {
       bootstrapTimelineAccountsField.parentElement.classList.add('disabled');
+      bootstrapTimelineAccountsField.parentElement.parentElement.classList.add('disabled');
     }
   }
 };
 
 delegate(document, '#form_admin_settings_enable_bootstrap_timeline_accounts', 'change', ({ target }) => onEnableBootstrapTimelineAccountsChange(target));
 
+const onChangeRegistrationMode = (target) => {
+  const enabled = target.value === 'approved';
+
+  [].forEach.call(document.querySelectorAll('#form_admin_settings_require_invite_text'), (input) => {
+    input.disabled = !enabled;
+    if (enabled) {
+      let element = input;
+      do {
+        element.classList.remove('disabled');
+        element = element.parentElement;
+      } while (element && !element.classList.contains('fields-group'));
+    } else {
+      let element = input;
+      do {
+        element.classList.add('disabled');
+        element = element.parentElement;
+      } while (element && !element.classList.contains('fields-group'));
+    }
+  });
+};
+
+delegate(document, '#form_admin_settings_registrations_mode', 'change', ({ target }) => onChangeRegistrationMode(target));
+
 ready(() => {
   const domainBlockSeverityInput = document.getElementById('domain_block_severity');
   if (domainBlockSeverityInput) onDomainBlockSeverityChange(domainBlockSeverityInput);
 
   const enableBootstrapTimelineAccounts = document.getElementById('form_admin_settings_enable_bootstrap_timeline_accounts');
   if (enableBootstrapTimelineAccounts) onEnableBootstrapTimelineAccountsChange(enableBootstrapTimelineAccounts);
+
+  const registrationMode = document.getElementById('form_admin_settings_registrations_mode');
+  if (registrationMode) onChangeRegistrationMode(registrationMode);
 });
diff --git a/app/javascript/flavours/glitch/actions/notifications.js b/app/javascript/flavours/glitch/actions/notifications.js
index 9f12df773..bd3a34e5d 100644
--- a/app/javascript/flavours/glitch/actions/notifications.js
+++ b/app/javascript/flavours/glitch/actions/notifications.js
@@ -50,9 +50,8 @@ export const NOTIFICATIONS_SET_VISIBILITY = 'NOTIFICATIONS_SET_VISIBILITY';
 
 export const NOTIFICATIONS_MARK_AS_READ = 'NOTIFICATIONS_MARK_AS_READ';
 
-export const NOTIFICATIONS_SET_BROWSER_SUPPORT        = 'NOTIFICATIONS_SET_BROWSER_SUPPORT';
-export const NOTIFICATIONS_SET_BROWSER_PERMISSION     = 'NOTIFICATIONS_SET_BROWSER_PERMISSION';
-export const NOTIFICATIONS_DISMISS_BROWSER_PERMISSION = 'NOTIFICATIONS_DISMISS_BROWSER_PERMISSION';
+export const NOTIFICATIONS_SET_BROWSER_SUPPORT    = 'NOTIFICATIONS_SET_BROWSER_SUPPORT';
+export const NOTIFICATIONS_SET_BROWSER_PERMISSION = 'NOTIFICATIONS_SET_BROWSER_PERMISSION';
 
 defineMessages({
   mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' },
@@ -372,7 +371,3 @@ export function setBrowserPermission (value) {
     value,
   };
 }
-
-export const dismissBrowserPermission = () => ({
-  type: NOTIFICATIONS_DISMISS_BROWSER_PERMISSION,
-});
diff --git a/app/javascript/flavours/glitch/components/autosuggest_input.js b/app/javascript/flavours/glitch/components/autosuggest_input.js
index 1ef7ee216..cc0ff7dea 100644
--- a/app/javascript/flavours/glitch/components/autosuggest_input.js
+++ b/app/javascript/flavours/glitch/components/autosuggest_input.js
@@ -4,7 +4,6 @@ import AutosuggestEmoji from './autosuggest_emoji';
 import AutosuggestHashtag from './autosuggest_hashtag';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
-import { isRtl } from 'flavours/glitch/util/rtl';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import classNames from 'classnames';
 import { List as ImmutableList } from 'immutable';
@@ -189,11 +188,6 @@ export default class AutosuggestInput extends ImmutablePureComponent {
   render () {
     const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, className, id, maxLength } = this.props;
     const { suggestionsHidden } = this.state;
-    const style = { direction: 'ltr' };
-
-    if (isRtl(value)) {
-      style.direction = 'rtl';
-    }
 
     return (
       <div className='autosuggest-input'>
@@ -212,7 +206,7 @@ export default class AutosuggestInput extends ImmutablePureComponent {
             onKeyUp={onKeyUp}
             onFocus={this.onFocus}
             onBlur={this.onBlur}
-            style={style}
+            dir='auto'
             aria-autocomplete='list'
             id={id}
             className={className}
diff --git a/app/javascript/flavours/glitch/components/autosuggest_textarea.js b/app/javascript/flavours/glitch/components/autosuggest_textarea.js
index 1ce2f42b4..967c593af 100644
--- a/app/javascript/flavours/glitch/components/autosuggest_textarea.js
+++ b/app/javascript/flavours/glitch/components/autosuggest_textarea.js
@@ -4,7 +4,6 @@ import AutosuggestEmoji from './autosuggest_emoji';
 import AutosuggestHashtag from './autosuggest_hashtag';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
-import { isRtl } from 'flavours/glitch/util/rtl';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import Textarea from 'react-textarea-autosize';
 import classNames from 'classnames';
@@ -195,11 +194,6 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
   render () {
     const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, children } = this.props;
     const { suggestionsHidden } = this.state;
-    const style = { direction: 'ltr' };
-
-    if (isRtl(value)) {
-      style.direction = 'rtl';
-    }
 
     return [
       <div className='compose-form__autosuggest-wrapper' key='autosuggest-wrapper'>
@@ -220,7 +214,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
               onFocus={this.onFocus}
               onBlur={this.onBlur}
               onPaste={this.onPaste}
-              style={style}
+              dir='auto'
               aria-autocomplete='list'
             />
           </label>
diff --git a/app/javascript/flavours/glitch/components/status_content.js b/app/javascript/flavours/glitch/components/status_content.js
index a39f747b8..76e2d79a5 100644
--- a/app/javascript/flavours/glitch/components/status_content.js
+++ b/app/javascript/flavours/glitch/components/status_content.js
@@ -1,7 +1,6 @@
 import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
-import { isRtl } from 'flavours/glitch/util/rtl';
 import { FormattedMessage } from 'react-intl';
 import Permalink from './permalink';
 import classnames from 'classnames';
@@ -277,16 +276,11 @@ export default class StatusContent extends React.PureComponent {
 
     const content = { __html: status.get('contentHtml') };
     const spoilerContent = { __html: status.get('spoilerHtml') };
-    const directionStyle = { direction: 'ltr' };
     const classNames = classnames('status__content', {
       'status__content--with-action': parseClick && !disabled,
       'status__content--with-spoiler': status.get('spoiler_text').length > 0,
     });
 
-    if (isRtl(status.get('search_index'))) {
-      directionStyle.direction = 'rtl';
-    }
-
     if (status.get('spoiler_text').length > 0) {
       let mentionsPlaceholder = '';
 
@@ -346,7 +340,6 @@ export default class StatusContent extends React.PureComponent {
             <div
               ref={this.setContentsRef}
               key={`contents-${tagLinks}`}
-              style={directionStyle}
               tabIndex={!hidden ? 0 : null}
               dangerouslySetInnerHTML={content}
               className='status__content__text'
@@ -360,7 +353,6 @@ export default class StatusContent extends React.PureComponent {
       return (
         <div
           className={classNames}
-          style={directionStyle}
           onMouseDown={this.handleMouseDown}
           onMouseUp={this.handleMouseUp}
           tabIndex='0'
@@ -380,7 +372,6 @@ export default class StatusContent extends React.PureComponent {
       return (
         <div
           className='status__content'
-          style={directionStyle}
           tabIndex='0'
           ref={this.setRef}
         >
diff --git a/app/javascript/flavours/glitch/features/account_gallery/index.js b/app/javascript/flavours/glitch/features/account_gallery/index.js
index fda8082cc..81203e3f8 100644
--- a/app/javascript/flavours/glitch/features/account_gallery/index.js
+++ b/app/javascript/flavours/glitch/features/account_gallery/index.js
@@ -168,7 +168,7 @@ class AccountGallery extends ImmutablePureComponent {
 
             {suspended ? (
               <div className='empty-column-indicator'>
-                <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />
+                <FormattedMessage id='empty_column.account_suspended' defaultMessage='Account suspended' />
               </div>
             ) : (
               <div role='feed' className='account-gallery__container' ref={this.handleRef}>
diff --git a/app/javascript/flavours/glitch/features/account_timeline/index.js b/app/javascript/flavours/glitch/features/account_timeline/index.js
index c56cc9b8e..0d24980a9 100644
--- a/app/javascript/flavours/glitch/features/account_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/account_timeline/index.js
@@ -117,7 +117,7 @@ class AccountTimeline extends ImmutablePureComponent {
     let emptyMessage;
 
     if (suspended) {
-      emptyMessage = <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />;
+      emptyMessage = <FormattedMessage id='empty_column.account_suspended' defaultMessage='Account suspended' />;
     } else if (remote && statusIds.isEmpty()) {
       emptyMessage = <RemoteHint url={remoteUrl} />;
     } else {
diff --git a/app/javascript/flavours/glitch/features/compose/components/reply_indicator.js b/app/javascript/flavours/glitch/features/compose/components/reply_indicator.js
index 9d5b65a40..0fd07c282 100644
--- a/app/javascript/flavours/glitch/features/compose/components/reply_indicator.js
+++ b/app/javascript/flavours/glitch/features/compose/components/reply_indicator.js
@@ -10,9 +10,6 @@ import AccountContainer from 'flavours/glitch/containers/account_container';
 import IconButton from 'flavours/glitch/components/icon_button';
 import AttachmentList from 'flavours/glitch/components/attachment_list';
 
-//  Utils.
-import { isRtl } from 'flavours/glitch/util/rtl';
-
 //  Messages.
 const messages = defineMessages({
   cancel: {
@@ -71,7 +68,6 @@ class ReplyIndicator extends ImmutablePureComponent {
         <div
           className='content'
           dangerouslySetInnerHTML={{ __html: content || '' }}
-          style={{ direction: isRtl(content) ? 'rtl' : 'ltr' }}
         />
         {attachments.size > 0 && (
           <AttachmentList
diff --git a/app/javascript/flavours/glitch/features/notifications/components/column_settings.js b/app/javascript/flavours/glitch/features/notifications/components/column_settings.js
index 9748219dd..e502c3173 100644
--- a/app/javascript/flavours/glitch/features/notifications/components/column_settings.js
+++ b/app/javascript/flavours/glitch/features/notifications/components/column_settings.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { FormattedMessage } from 'react-intl';
 import ClearColumnButton from './clear_column_button';
+import GrantPermissionButton from './grant_permission_button';
 import SettingToggle from './setting_toggle';
 
 export default class ColumnSettings extends React.PureComponent {
@@ -23,7 +24,7 @@ export default class ColumnSettings extends React.PureComponent {
   }
 
   render () {
-    const { settings, pushSettings, onChange, onClear, alertsEnabled, browserSupport, browserPermission } = this.props;
+    const { settings, pushSettings, onChange, onClear, alertsEnabled, browserSupport, browserPermission, onRequestNotificationPermission } = this.props;
 
     const filterShowStr = <FormattedMessage id='notifications.column_settings.filter_bar.show' defaultMessage='Show' />;
     const filterAdvancedStr = <FormattedMessage id='notifications.column_settings.filter_bar.advanced' defaultMessage='Display all categories' />;
@@ -43,6 +44,14 @@ export default class ColumnSettings extends React.PureComponent {
           </div>
         )}
 
+        {alertsEnabled && browserSupport && browserPermission === 'default' && (
+          <div className='column-settings__row column-settings__row--with-margin'>
+            <span className='warning-hint'>
+              <FormattedMessage id='notifications.permission_required' defaultMessage='Desktop notifications are unavailable because the required permission has not been granted.' /> <GrantPermissionButton onClick={onRequestNotificationPermission} />
+            </span>
+          </div>
+        )}
+
         <div className='column-settings__row'>
           <ClearColumnButton onClick={onClear} />
         </div>
@@ -62,7 +71,7 @@ export default class ColumnSettings extends React.PureComponent {
           <span id='notifications-follow' className='column-settings__section'><FormattedMessage id='notifications.column_settings.follow' defaultMessage='New followers:' /></span>
 
           <div className='column-settings__row'>
-            <SettingToggle disabled={browserPermission !== 'granted'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'follow']} onChange={onChange} label={alertStr} />
+            <SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'follow']} onChange={onChange} label={alertStr} />
             {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'follow']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
             <SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'follow']} onChange={onChange} label={showStr} />
             <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'follow']} onChange={onChange} label={soundStr} />
@@ -73,7 +82,7 @@ export default class ColumnSettings extends React.PureComponent {
           <span id='notifications-follow-request' className='column-settings__section'><FormattedMessage id='notifications.column_settings.follow_request' defaultMessage='New follow requests:' /></span>
 
           <div className='column-settings__row'>
-            <SettingToggle disabled={browserPermission !== 'granted'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'follow_request']} onChange={onChange} label={alertStr} />
+            <SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'follow_request']} onChange={onChange} label={alertStr} />
             {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'follow_request']} onChange={this.onPushChange} label={pushStr} />}
             <SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'follow_request']} onChange={onChange} label={showStr} />
             <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'follow_request']} onChange={onChange} label={soundStr} />
@@ -84,7 +93,7 @@ export default class ColumnSettings extends React.PureComponent {
           <span id='notifications-favourite' className='column-settings__section'><FormattedMessage id='notifications.column_settings.favourite' defaultMessage='Favourites:' /></span>
 
           <div className='column-settings__row'>
-            <SettingToggle disabled={browserPermission !== 'granted'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'favourite']} onChange={onChange} label={alertStr} />
+            <SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'favourite']} onChange={onChange} label={alertStr} />
             {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'favourite']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
             <SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'favourite']} onChange={onChange} label={showStr} />
             <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'favourite']} onChange={onChange} label={soundStr} />
@@ -95,7 +104,7 @@ export default class ColumnSettings extends React.PureComponent {
           <span id='notifications-mention' className='column-settings__section'><FormattedMessage id='notifications.column_settings.mention' defaultMessage='Mentions:' /></span>
 
           <div className='column-settings__row'>
-            <SettingToggle disabled={browserPermission !== 'granted'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'mention']} onChange={onChange} label={alertStr} />
+            <SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'mention']} onChange={onChange} label={alertStr} />
             {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'mention']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
             <SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'mention']} onChange={onChange} label={showStr} />
             <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'mention']} onChange={onChange} label={soundStr} />
@@ -106,7 +115,7 @@ export default class ColumnSettings extends React.PureComponent {
           <span id='notifications-reblog' className='column-settings__section'><FormattedMessage id='notifications.column_settings.reblog' defaultMessage='Boosts:' /></span>
 
           <div className='column-settings__row'>
-            <SettingToggle disabled={browserPermission !== 'granted'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'reblog']} onChange={onChange} label={alertStr} />
+            <SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'reblog']} onChange={onChange} label={alertStr} />
             {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'reblog']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
             <SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'reblog']} onChange={onChange} label={showStr} />
             <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'reblog']} onChange={onChange} label={soundStr} />
@@ -117,7 +126,7 @@ export default class ColumnSettings extends React.PureComponent {
           <span id='notifications-poll' className='column-settings__section'><FormattedMessage id='notifications.column_settings.poll' defaultMessage='Poll results:' /></span>
 
           <div className='column-settings__row'>
-            <SettingToggle disabled={browserPermission !== 'granted'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'poll']} onChange={onChange} label={alertStr} />
+            <SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'poll']} onChange={onChange} label={alertStr} />
             {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'poll']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
             <SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'poll']} onChange={onChange} label={showStr} />
             <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'poll']} onChange={onChange} label={soundStr} />
@@ -128,7 +137,7 @@ export default class ColumnSettings extends React.PureComponent {
           <span id='notifications-status' className='column-settings__section'><FormattedMessage id='notifications.column_settings.status' defaultMessage='New toots:' /></span>
 
           <div className='column-settings__row'>
-            <SettingToggle disabled={browserPermission !== 'granted'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'status']} onChange={onChange} label={alertStr} />
+            <SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'status']} onChange={onChange} label={alertStr} />
             {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'status']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
             <SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'status']} onChange={onChange} label={showStr} />
             <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'status']} onChange={onChange} label={soundStr} />
diff --git a/app/javascript/flavours/glitch/features/notifications/components/grant_permission_button.js b/app/javascript/flavours/glitch/features/notifications/components/grant_permission_button.js
new file mode 100644
index 000000000..798e4c787
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/notifications/components/grant_permission_button.js
@@ -0,0 +1,19 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { FormattedMessage } from 'react-intl';
+
+export default class GrantPermissionButton extends React.PureComponent {
+
+  static propTypes = {
+    onClick: PropTypes.func.isRequired,
+  };
+
+  render () {
+    return (
+      <button className='text-btn column-header__permission-btn' tabIndex='0' onClick={this.props.onClick}>
+        <FormattedMessage id='notifications.grant_permission' defaultMessage='Grant permission.' />
+      </button>
+    );
+  }
+
+}
diff --git a/app/javascript/flavours/glitch/features/notifications/components/notifications_permission_banner.js b/app/javascript/flavours/glitch/features/notifications/components/notifications_permission_banner.js
index 73fc05dea..dd163225e 100644
--- a/app/javascript/flavours/glitch/features/notifications/components/notifications_permission_banner.js
+++ b/app/javascript/flavours/glitch/features/notifications/components/notifications_permission_banner.js
@@ -2,7 +2,8 @@ import React from 'react';
 import Icon from 'flavours/glitch/components/icon';
 import Button from 'flavours/glitch/components/button';
 import IconButton from 'flavours/glitch/components/icon_button';
-import { requestBrowserPermission, dismissBrowserPermission } from 'flavours/glitch/actions/notifications';
+import { requestBrowserPermission } from 'flavours/glitch/actions/notifications';
+import { changeSetting } from 'flavours/glitch/actions/settings';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
@@ -25,7 +26,7 @@ class NotificationsPermissionBanner extends React.PureComponent {
   }
 
   handleClose = () => {
-    this.props.dispatch(dismissBrowserPermission());
+    this.props.dispatch(changeSetting(['notifications', 'dismissPermissionBanner'], true));
   }
 
   render () {
diff --git a/app/javascript/flavours/glitch/features/notifications/index.js b/app/javascript/flavours/glitch/features/notifications/index.js
index 73d969517..5ceda9a91 100644
--- a/app/javascript/flavours/glitch/features/notifications/index.js
+++ b/app/javascript/flavours/glitch/features/notifications/index.js
@@ -69,7 +69,7 @@ const mapStateToProps = state => ({
   notifCleaningActive: state.getIn(['notifications', 'cleaningMode']),
   lastReadId: state.getIn(['local_settings', 'notifications', 'show_unread']) ? state.getIn(['notifications', 'readMarkerId']) : '0',
   canMarkAsRead: state.getIn(['local_settings', 'notifications', 'show_unread']) && state.getIn(['notifications', 'readMarkerId']) !== '0' && getNotifications(state).some(item => item !== null && compareId(item.get('id'), state.getIn(['notifications', 'readMarkerId'])) > 0),
-  needsNotificationPermission: state.getIn(['settings', 'notifications', 'alerts']).includes(true) && state.getIn(['notifications', 'browserSupport']) && state.getIn(['notifications', 'browserPermission']) === 'default',
+  needsNotificationPermission: state.getIn(['settings', 'notifications', 'alerts']).includes(true) && state.getIn(['notifications', 'browserSupport']) && state.getIn(['notifications', 'browserPermission']) === 'default' && !state.getIn(['settings', 'notifications', 'dismissPermissionBanner']),
 });
 
 /* glitch */
diff --git a/app/javascript/flavours/glitch/reducers/notifications.js b/app/javascript/flavours/glitch/reducers/notifications.js
index c630cc7e3..b4c5ef71a 100644
--- a/app/javascript/flavours/glitch/reducers/notifications.js
+++ b/app/javascript/flavours/glitch/reducers/notifications.js
@@ -19,7 +19,6 @@ import {
   NOTIFICATIONS_MARK_AS_READ,
   NOTIFICATIONS_SET_BROWSER_SUPPORT,
   NOTIFICATIONS_SET_BROWSER_PERMISSION,
-  NOTIFICATIONS_DISMISS_BROWSER_PERMISSION,
 } from 'flavours/glitch/actions/notifications';
 import {
   ACCOUNT_BLOCK_SUCCESS,
@@ -284,8 +283,6 @@ export default function notifications(state = initialState, action) {
     return state.set('browserSupport', action.value);
   case NOTIFICATIONS_SET_BROWSER_PERMISSION:
     return state.set('browserPermission', action.value);
-  case NOTIFICATIONS_DISMISS_BROWSER_PERMISSION:
-    return state.set('browserPermission', 'denied');
 
   case NOTIFICATION_MARK_FOR_DELETE:
     return markForDelete(state, action.id, action.yes);
diff --git a/app/javascript/flavours/glitch/reducers/settings.js b/app/javascript/flavours/glitch/reducers/settings.js
index bf0545c48..091b8feec 100644
--- a/app/javascript/flavours/glitch/reducers/settings.js
+++ b/app/javascript/flavours/glitch/reducers/settings.js
@@ -48,6 +48,8 @@ const initialState = ImmutableMap({
       advanced: false,
     }),
 
+    dismissPermissionBanner: false,
+
     shows: ImmutableMap({
       follow: true,
       follow_request: false,
diff --git a/app/javascript/flavours/glitch/styles/about.scss b/app/javascript/flavours/glitch/styles/about.scss
index ac5f3ebb0..de821fbe4 100644
--- a/app/javascript/flavours/glitch/styles/about.scss
+++ b/app/javascript/flavours/glitch/styles/about.scss
@@ -734,6 +734,7 @@ $small-breakpoint: 960px;
 
       &__column {
         flex: 1 1 50%;
+        overflow-x: hidden;
       }
     }
 
diff --git a/app/javascript/flavours/glitch/styles/components/columns.scss b/app/javascript/flavours/glitch/styles/components/columns.scss
index 2550f50f4..2d080d3b9 100644
--- a/app/javascript/flavours/glitch/styles/components/columns.scss
+++ b/app/javascript/flavours/glitch/styles/components/columns.scss
@@ -443,6 +443,12 @@
   }
 }
 
+.column-header__permission-btn {
+  display: inline;
+  font-weight: inherit;
+  text-decoration: underline;
+}
+
 .column-header__setting-arrows {
   float: right;
 
@@ -690,7 +696,7 @@
 }
 
 .column-settings__row {
-  .text-btn {
+  .text-btn:not(.column-header__permission-btn) {
     margin-bottom: 15px;
   }
 }
diff --git a/app/javascript/flavours/glitch/styles/components/status.scss b/app/javascript/flavours/glitch/styles/components/status.scss
index 7ca1ae423..e067c9335 100644
--- a/app/javascript/flavours/glitch/styles/components/status.scss
+++ b/app/javascript/flavours/glitch/styles/components/status.scss
@@ -70,6 +70,7 @@
   p, pre, blockquote {
     margin-bottom: 20px;
     white-space: pre-wrap;
+    unicode-bidi: plaintext;
 
     &:last-child {
       margin-bottom: 0;
@@ -152,6 +153,7 @@
   a {
     color: $secondary-text-color;
     text-decoration: none;
+    unicode-bidi: isolate;
 
     &:hover {
       text-decoration: underline;
diff --git a/app/javascript/flavours/glitch/styles/forms.scss b/app/javascript/flavours/glitch/styles/forms.scss
index f973cad22..0c7cb443b 100644
--- a/app/javascript/flavours/glitch/styles/forms.scss
+++ b/app/javascript/flavours/glitch/styles/forms.scss
@@ -362,11 +362,6 @@ code {
       box-shadow: none;
     }
 
-    &:focus:invalid:not(:placeholder-shown),
-    &:required:invalid:not(:placeholder-shown) {
-      border-color: lighten($error-red, 12%);
-    }
-
     &:required:valid {
       border-color: $valid-value-color;
     }
@@ -382,6 +377,16 @@ code {
     }
   }
 
+  input[type=text],
+  input[type=number],
+  input[type=email],
+  input[type=password] {
+    &:focus:invalid:not(:placeholder-shown),
+    &:required:invalid:not(:placeholder-shown) {
+      border-color: lighten($error-red, 12%);
+    }
+  }
+
   .input.field_with_errors {
     label {
       color: lighten($error-red, 12%);
diff --git a/app/javascript/flavours/glitch/util/rtl.js b/app/javascript/flavours/glitch/util/rtl.js
deleted file mode 100644
index 89bed6de8..000000000
--- a/app/javascript/flavours/glitch/util/rtl.js
+++ /dev/null
@@ -1,32 +0,0 @@
-// U+0590  to U+05FF  - Hebrew
-// U+0600  to U+06FF  - Arabic
-// U+0700  to U+074F  - Syriac
-// U+0750  to U+077F  - Arabic Supplement
-// U+0780  to U+07BF  - Thaana
-// U+07C0  to U+07FF  - N'Ko
-// U+0800  to U+083F  - Samaritan
-// U+08A0  to U+08FF  - Arabic Extended-A
-// U+FB1D  to U+FB4F  - Hebrew presentation forms
-// U+FB50  to U+FDFF  - Arabic presentation forms A
-// U+FE70  to U+FEFF  - Arabic presentation forms B
-
-const rtlChars = /[\u0590-\u083F]|[\u08A0-\u08FF]|[\uFB1D-\uFDFF]|[\uFE70-\uFEFF]/mg;
-
-export function isRtl(text) {
-  if (text.length === 0) {
-    return false;
-  }
-
-  text = text.replace(/(?:^|[^\/\w])@([a-z0-9_]+(@[a-z0-9\.\-]+)?)/ig, '');
-  text = text.replace(/(?:^|[^\/\w])#([\S]+)/ig, '');
-  text = text.replace(/\s+/g, '');
-  text = text.replace(/(\w\S+\.\w{2,}\S*)/g, '');
-
-  const matches = text.match(rtlChars);
-
-  if (!matches) {
-    return false;
-  }
-
-  return matches.length / text.length > 0.3;
-};
diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js
index 93e18fba9..3464ac995 100644
--- a/app/javascript/mastodon/actions/notifications.js
+++ b/app/javascript/mastodon/actions/notifications.js
@@ -37,9 +37,8 @@ export const NOTIFICATIONS_UNMOUNT = 'NOTIFICATIONS_UNMOUNT';
 
 export const NOTIFICATIONS_MARK_AS_READ = 'NOTIFICATIONS_MARK_AS_READ';
 
-export const NOTIFICATIONS_SET_BROWSER_SUPPORT        = 'NOTIFICATIONS_SET_BROWSER_SUPPORT';
-export const NOTIFICATIONS_SET_BROWSER_PERMISSION     = 'NOTIFICATIONS_SET_BROWSER_PERMISSION';
-export const NOTIFICATIONS_DISMISS_BROWSER_PERMISSION = 'NOTIFICATIONS_DISMISS_BROWSER_PERMISSION';
+export const NOTIFICATIONS_SET_BROWSER_SUPPORT    = 'NOTIFICATIONS_SET_BROWSER_SUPPORT';
+export const NOTIFICATIONS_SET_BROWSER_PERMISSION = 'NOTIFICATIONS_SET_BROWSER_PERMISSION';
 
 defineMessages({
   mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' },
@@ -284,7 +283,3 @@ export function setBrowserPermission (value) {
     value,
   };
 }
-
-export const dismissBrowserPermission = () => ({
-  type: NOTIFICATIONS_DISMISS_BROWSER_PERMISSION,
-});
diff --git a/app/javascript/mastodon/components/autosuggest_input.js b/app/javascript/mastodon/components/autosuggest_input.js
index 6d2035add..5187f95c8 100644
--- a/app/javascript/mastodon/components/autosuggest_input.js
+++ b/app/javascript/mastodon/components/autosuggest_input.js
@@ -4,7 +4,6 @@ import AutosuggestEmoji from './autosuggest_emoji';
 import AutosuggestHashtag from './autosuggest_hashtag';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
-import { isRtl } from '../rtl';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import classNames from 'classnames';
 import { List as ImmutableList } from 'immutable';
@@ -189,11 +188,6 @@ export default class AutosuggestInput extends ImmutablePureComponent {
   render () {
     const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, className, id, maxLength } = this.props;
     const { suggestionsHidden } = this.state;
-    const style = { direction: 'ltr' };
-
-    if (isRtl(value)) {
-      style.direction = 'rtl';
-    }
 
     return (
       <div className='autosuggest-input'>
@@ -212,7 +206,7 @@ export default class AutosuggestInput extends ImmutablePureComponent {
             onKeyUp={onKeyUp}
             onFocus={this.onFocus}
             onBlur={this.onBlur}
-            style={style}
+            dir='auto'
             aria-autocomplete='list'
             id={id}
             className={className}
diff --git a/app/javascript/mastodon/components/autosuggest_textarea.js b/app/javascript/mastodon/components/autosuggest_textarea.js
index 58ec4f6eb..08b9cd80b 100644
--- a/app/javascript/mastodon/components/autosuggest_textarea.js
+++ b/app/javascript/mastodon/components/autosuggest_textarea.js
@@ -4,7 +4,6 @@ import AutosuggestEmoji from './autosuggest_emoji';
 import AutosuggestHashtag from './autosuggest_hashtag';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
-import { isRtl } from '../rtl';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import Textarea from 'react-textarea-autosize';
 import classNames from 'classnames';
@@ -195,11 +194,6 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
   render () {
     const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, children } = this.props;
     const { suggestionsHidden } = this.state;
-    const style = { direction: 'ltr' };
-
-    if (isRtl(value)) {
-      style.direction = 'rtl';
-    }
 
     return [
       <div className='compose-form__autosuggest-wrapper' key='autosuggest-wrapper'>
@@ -220,7 +214,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
               onFocus={this.onFocus}
               onBlur={this.onBlur}
               onPaste={this.onPaste}
-              style={style}
+              dir='auto'
               aria-autocomplete='list'
             />
           </label>
diff --git a/app/javascript/mastodon/components/status_content.js b/app/javascript/mastodon/components/status_content.js
index 3200f2d82..185a2a663 100644
--- a/app/javascript/mastodon/components/status_content.js
+++ b/app/javascript/mastodon/components/status_content.js
@@ -1,7 +1,6 @@
 import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
-import { isRtl } from '../rtl';
 import { FormattedMessage } from 'react-intl';
 import Permalink from './permalink';
 import classnames from 'classnames';
@@ -186,17 +185,12 @@ export default class StatusContent extends React.PureComponent {
 
     const content = { __html: status.get('contentHtml') };
     const spoilerContent = { __html: status.get('spoilerHtml') };
-    const directionStyle = { direction: 'ltr' };
     const classNames = classnames('status__content', {
       'status__content--with-action': this.props.onClick && this.context.router,
       'status__content--with-spoiler': status.get('spoiler_text').length > 0,
       'status__content--collapsed': renderReadMore,
     });
 
-    if (isRtl(status.get('search_index'))) {
-      directionStyle.direction = 'rtl';
-    }
-
     const showThreadButton = (
       <button className='status__content__read-more-button' onClick={this.props.onClick}>
         <FormattedMessage id='status.show_thread' defaultMessage='Show thread' />
@@ -225,7 +219,7 @@ export default class StatusContent extends React.PureComponent {
       }
 
       return (
-        <div className={classNames} ref={this.setRef} tabIndex='0' style={directionStyle} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
+        <div className={classNames} ref={this.setRef} tabIndex='0' onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
           <p style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}>
             <span dangerouslySetInnerHTML={spoilerContent} />
             {' '}
@@ -234,7 +228,7 @@ export default class StatusContent extends React.PureComponent {
 
           {mentionsPlaceholder}
 
-          <div tabIndex={!hidden ? 0 : null} className={`status__content__text ${!hidden ? 'status__content__text--visible' : ''}`} style={directionStyle} dangerouslySetInnerHTML={content} />
+          <div tabIndex={!hidden ? 0 : null} className={`status__content__text ${!hidden ? 'status__content__text--visible' : ''}`} dangerouslySetInnerHTML={content} />
 
           {!hidden && !!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
 
@@ -243,8 +237,8 @@ export default class StatusContent extends React.PureComponent {
       );
     } else if (this.props.onClick) {
       const output = [
-        <div className={classNames} ref={this.setRef} tabIndex='0' style={directionStyle} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} key='status-content'>
-          <div className='status__content__text status__content__text--visible' style={directionStyle} dangerouslySetInnerHTML={content} />
+        <div className={classNames} ref={this.setRef} tabIndex='0' onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} key='status-content'>
+          <div className='status__content__text status__content__text--visible' dangerouslySetInnerHTML={content} />
 
           {!!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
 
@@ -259,8 +253,8 @@ export default class StatusContent extends React.PureComponent {
       return output;
     } else {
       return (
-        <div className={classNames} ref={this.setRef} tabIndex='0' style={directionStyle}>
-          <div className='status__content__text status__content__text--visible' style={directionStyle} dangerouslySetInnerHTML={content} />
+        <div className={classNames} ref={this.setRef} tabIndex='0'>
+          <div className='status__content__text status__content__text--visible' dangerouslySetInnerHTML={content} />
 
           {!!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
 
diff --git a/app/javascript/mastodon/features/account_gallery/index.js b/app/javascript/mastodon/features/account_gallery/index.js
index 597ca8af6..015a6a6d7 100644
--- a/app/javascript/mastodon/features/account_gallery/index.js
+++ b/app/javascript/mastodon/features/account_gallery/index.js
@@ -152,6 +152,14 @@ class AccountGallery extends ImmutablePureComponent {
       loadOlder = <LoadMore visible={!isLoading} onClick={this.handleLoadOlder} />;
     }
 
+    let emptyMessage;
+
+    if (suspended) {
+      emptyMessage = <FormattedMessage id='empty_column.account_suspended' defaultMessage='Account suspended' />;
+    } else if (blockedBy) {
+      emptyMessage = <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />;
+    }
+
     return (
       <Column>
         <ColumnBackButton multiColumn={multiColumn} />
@@ -162,7 +170,7 @@ class AccountGallery extends ImmutablePureComponent {
 
             {(suspended || blockedBy) ? (
               <div className='empty-column-indicator'>
-                <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />
+                {emptyMessage}
               </div>
             ) : (
               <div role='feed' className='account-gallery__container' ref={this.handleRef}>
diff --git a/app/javascript/mastodon/features/account_timeline/index.js b/app/javascript/mastodon/features/account_timeline/index.js
index cbc859805..fa4239d6f 100644
--- a/app/javascript/mastodon/features/account_timeline/index.js
+++ b/app/javascript/mastodon/features/account_timeline/index.js
@@ -136,7 +136,9 @@ class AccountTimeline extends ImmutablePureComponent {
 
     let emptyMessage;
 
-    if (suspended || blockedBy) {
+    if (suspended) {
+      emptyMessage = <FormattedMessage id='empty_column.account_suspended' defaultMessage='Account suspended' />;
+    } else if (blockedBy) {
       emptyMessage = <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />;
     } else if (remote && statusIds.isEmpty()) {
       emptyMessage = <RemoteHint url={remoteUrl} />;
diff --git a/app/javascript/mastodon/features/compose/components/reply_indicator.js b/app/javascript/mastodon/features/compose/components/reply_indicator.js
index 66dc85742..856383893 100644
--- a/app/javascript/mastodon/features/compose/components/reply_indicator.js
+++ b/app/javascript/mastodon/features/compose/components/reply_indicator.js
@@ -6,7 +6,6 @@ import IconButton from '../../../components/icon_button';
 import DisplayName from '../../../components/display_name';
 import { defineMessages, injectIntl } from 'react-intl';
 import ImmutablePureComponent from 'react-immutable-pure-component';
-import { isRtl } from '../../../rtl';
 import AttachmentList from 'mastodon/components/attachment_list';
 
 const messages = defineMessages({
@@ -45,9 +44,6 @@ class ReplyIndicator extends ImmutablePureComponent {
     }
 
     const content = { __html: status.get('contentHtml') };
-    const style   = {
-      direction: isRtl(status.get('search_index')) ? 'rtl' : 'ltr',
-    };
 
     return (
       <div className='reply-indicator'>
@@ -60,7 +56,7 @@ class ReplyIndicator extends ImmutablePureComponent {
           </a>
         </div>
 
-        <div className='reply-indicator__content' style={style} dangerouslySetInnerHTML={content} />
+        <div className='reply-indicator__content' dangerouslySetInnerHTML={content} />
 
         {status.get('media_attachments').size > 0 && (
           <AttachmentList
diff --git a/app/javascript/mastodon/features/notifications/components/column_settings.js b/app/javascript/mastodon/features/notifications/components/column_settings.js
index 169e4b44d..8339a367e 100644
--- a/app/javascript/mastodon/features/notifications/components/column_settings.js
+++ b/app/javascript/mastodon/features/notifications/components/column_settings.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { FormattedMessage } from 'react-intl';
 import ClearColumnButton from './clear_column_button';
+import GrantPermissionButton from './grant_permission_button';
 import SettingToggle from './setting_toggle';
 
 export default class ColumnSettings extends React.PureComponent {
@@ -23,7 +24,7 @@ export default class ColumnSettings extends React.PureComponent {
   }
 
   render () {
-    const { settings, pushSettings, onChange, onClear, alertsEnabled, browserSupport, browserPermission } = this.props;
+    const { settings, pushSettings, onChange, onClear, alertsEnabled, browserSupport, browserPermission, onRequestNotificationPermission } = this.props;
 
     const filterShowStr = <FormattedMessage id='notifications.column_settings.filter_bar.show' defaultMessage='Show' />;
     const filterAdvancedStr = <FormattedMessage id='notifications.column_settings.filter_bar.advanced' defaultMessage='Display all categories' />;
@@ -42,6 +43,14 @@ export default class ColumnSettings extends React.PureComponent {
           </div>
         )}
 
+        {alertsEnabled && browserSupport && browserPermission === 'default' && (
+          <div className='column-settings__row column-settings__row--with-margin'>
+            <span className='warning-hint'>
+              <FormattedMessage id='notifications.permission_required' defaultMessage='Desktop notifications are unavailable because the required permission has not been granted.' /> <GrantPermissionButton onClick={onRequestNotificationPermission} />
+            </span>
+          </div>
+        )}
+
         <div className='column-settings__row'>
           <ClearColumnButton onClick={onClear} />
         </div>
@@ -61,7 +70,7 @@ export default class ColumnSettings extends React.PureComponent {
           <span id='notifications-follow' className='column-settings__section'><FormattedMessage id='notifications.column_settings.follow' defaultMessage='New followers:' /></span>
 
           <div className='column-settings__row'>
-            <SettingToggle disabled={browserPermission !== 'granted'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'follow']} onChange={onChange} label={alertStr} />
+            <SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'follow']} onChange={onChange} label={alertStr} />
             {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'follow']} onChange={this.onPushChange} label={pushStr} />}
             <SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'follow']} onChange={onChange} label={showStr} />
             <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'follow']} onChange={onChange} label={soundStr} />
@@ -72,7 +81,7 @@ export default class ColumnSettings extends React.PureComponent {
           <span id='notifications-follow-request' className='column-settings__section'><FormattedMessage id='notifications.column_settings.follow_request' defaultMessage='New follow requests:' /></span>
 
           <div className='column-settings__row'>
-            <SettingToggle disabled={browserPermission !== 'granted'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'follow_request']} onChange={onChange} label={alertStr} />
+            <SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'follow_request']} onChange={onChange} label={alertStr} />
             {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'follow_request']} onChange={this.onPushChange} label={pushStr} />}
             <SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'follow_request']} onChange={onChange} label={showStr} />
             <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'follow_request']} onChange={onChange} label={soundStr} />
@@ -83,7 +92,7 @@ export default class ColumnSettings extends React.PureComponent {
           <span id='notifications-favourite' className='column-settings__section'><FormattedMessage id='notifications.column_settings.favourite' defaultMessage='Favourites:' /></span>
 
           <div className='column-settings__row'>
-            <SettingToggle disabled={browserPermission !== 'granted'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'favourite']} onChange={onChange} label={alertStr} />
+            <SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'favourite']} onChange={onChange} label={alertStr} />
             {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'favourite']} onChange={this.onPushChange} label={pushStr} />}
             <SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'favourite']} onChange={onChange} label={showStr} />
             <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'favourite']} onChange={onChange} label={soundStr} />
@@ -94,7 +103,7 @@ export default class ColumnSettings extends React.PureComponent {
           <span id='notifications-mention' className='column-settings__section'><FormattedMessage id='notifications.column_settings.mention' defaultMessage='Mentions:' /></span>
 
           <div className='column-settings__row'>
-            <SettingToggle disabled={browserPermission !== 'granted'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'mention']} onChange={onChange} label={alertStr} />
+            <SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'mention']} onChange={onChange} label={alertStr} />
             {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'mention']} onChange={this.onPushChange} label={pushStr} />}
             <SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'mention']} onChange={onChange} label={showStr} />
             <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'mention']} onChange={onChange} label={soundStr} />
@@ -105,7 +114,7 @@ export default class ColumnSettings extends React.PureComponent {
           <span id='notifications-reblog' className='column-settings__section'><FormattedMessage id='notifications.column_settings.reblog' defaultMessage='Boosts:' /></span>
 
           <div className='column-settings__row'>
-            <SettingToggle disabled={browserPermission !== 'granted'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'reblog']} onChange={onChange} label={alertStr} />
+            <SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'reblog']} onChange={onChange} label={alertStr} />
             {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'reblog']} onChange={this.onPushChange} label={pushStr} />}
             <SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'reblog']} onChange={onChange} label={showStr} />
             <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'reblog']} onChange={onChange} label={soundStr} />
@@ -116,7 +125,7 @@ export default class ColumnSettings extends React.PureComponent {
           <span id='notifications-poll' className='column-settings__section'><FormattedMessage id='notifications.column_settings.poll' defaultMessage='Poll results:' /></span>
 
           <div className='column-settings__row'>
-            <SettingToggle disabled={browserPermission !== 'granted'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'poll']} onChange={onChange} label={alertStr} />
+            <SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'poll']} onChange={onChange} label={alertStr} />
             {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'poll']} onChange={this.onPushChange} label={pushStr} />}
             <SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'poll']} onChange={onChange} label={showStr} />
             <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'poll']} onChange={onChange} label={soundStr} />
@@ -127,7 +136,7 @@ export default class ColumnSettings extends React.PureComponent {
           <span id='notifications-status' className='column-settings__section'><FormattedMessage id='notifications.column_settings.status' defaultMessage='New toots:' /></span>
 
           <div className='column-settings__row'>
-            <SettingToggle disabled={browserPermission !== 'granted'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'status']} onChange={onChange} label={alertStr} />
+            <SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'status']} onChange={onChange} label={alertStr} />
             {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'status']} onChange={this.onPushChange} label={pushStr} />}
             <SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'status']} onChange={onChange} label={showStr} />
             <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'status']} onChange={onChange} label={soundStr} />
diff --git a/app/javascript/mastodon/features/notifications/components/grant_permission_button.js b/app/javascript/mastodon/features/notifications/components/grant_permission_button.js
new file mode 100644
index 000000000..798e4c787
--- /dev/null
+++ b/app/javascript/mastodon/features/notifications/components/grant_permission_button.js
@@ -0,0 +1,19 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { FormattedMessage } from 'react-intl';
+
+export default class GrantPermissionButton extends React.PureComponent {
+
+  static propTypes = {
+    onClick: PropTypes.func.isRequired,
+  };
+
+  render () {
+    return (
+      <button className='text-btn column-header__permission-btn' tabIndex='0' onClick={this.props.onClick}>
+        <FormattedMessage id='notifications.grant_permission' defaultMessage='Grant permission.' />
+      </button>
+    );
+  }
+
+}
diff --git a/app/javascript/mastodon/features/notifications/components/notifications_permission_banner.js b/app/javascript/mastodon/features/notifications/components/notifications_permission_banner.js
index 6daf75814..df9b7fb1b 100644
--- a/app/javascript/mastodon/features/notifications/components/notifications_permission_banner.js
+++ b/app/javascript/mastodon/features/notifications/components/notifications_permission_banner.js
@@ -2,7 +2,8 @@ import React from 'react';
 import Icon from 'mastodon/components/icon';
 import Button from 'mastodon/components/button';
 import IconButton from 'mastodon/components/icon_button';
-import { requestBrowserPermission, dismissBrowserPermission } from 'mastodon/actions/notifications';
+import { requestBrowserPermission } from 'mastodon/actions/notifications';
+import { changeSetting } from 'mastodon/actions/settings';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
@@ -25,7 +26,7 @@ class NotificationsPermissionBanner extends React.PureComponent {
   }
 
   handleClose = () => {
-    this.props.dispatch(dismissBrowserPermission());
+    this.props.dispatch(changeSetting(['notifications', 'dismissPermissionBanner'], true));
   }
 
   render () {
diff --git a/app/javascript/mastodon/features/notifications/index.js b/app/javascript/mastodon/features/notifications/index.js
index 2e0afd863..108470c9a 100644
--- a/app/javascript/mastodon/features/notifications/index.js
+++ b/app/javascript/mastodon/features/notifications/index.js
@@ -62,7 +62,7 @@ const mapStateToProps = state => ({
   numPending: state.getIn(['notifications', 'pendingItems'], ImmutableList()).size,
   lastReadId: state.getIn(['notifications', 'readMarkerId']),
   canMarkAsRead: state.getIn(['notifications', 'readMarkerId']) !== '0' && getNotifications(state).some(item => item !== null && compareId(item.get('id'), state.getIn(['notifications', 'readMarkerId'])) > 0),
-  needsNotificationPermission: state.getIn(['settings', 'notifications', 'alerts']).includes(true) && state.getIn(['notifications', 'browserSupport']) && state.getIn(['notifications', 'browserPermission']) === 'default',
+  needsNotificationPermission: state.getIn(['settings', 'notifications', 'alerts']).includes(true) && state.getIn(['notifications', 'browserSupport']) && state.getIn(['notifications', 'browserPermission']) === 'default' && !state.getIn(['settings', 'notifications', 'dismissPermissionBanner']),
 });
 
 export default @connect(mapStateToProps)
diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index 8f0e04561..4d43a2720 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "نتائج البحث",
   "emoji_button.symbols": "رموز",
   "emoji_button.travel": "الأماكن والسفر",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "ليس هناك تبويقات!",
   "empty_column.account_unavailable": "الملف التعريفي غير متوفر",
   "empty_column.blocks": "لم تقم بحظر أي مستخدِم بعد.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "التالي",
   "lightbox.previous": "العودة",
-  "lightbox.view_context": "اعرض السياق",
   "lists.account.add": "أضف إلى القائمة",
   "lists.account.remove": "احذف من القائمة",
   "lists.delete": "احذف القائمة",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "تعديل العنوان",
   "lists.new.create": "إنشاء قائمة",
   "lists.new.title_placeholder": "عنوان القائمة الجديدة",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "إبحث في قائمة الحسابات التي تُتابِعها",
   "lists.subheading": "قوائمك",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "الإشارات",
   "notifications.filter.polls": "نتائج استطلاع الرأي",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} إشعارات",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/ast.json b/app/javascript/mastodon/locales/ast.json
index e0b67d6bb..4f5fec8b1 100644
--- a/app/javascript/mastodon/locales/ast.json
+++ b/app/javascript/mastodon/locales/ast.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Search results",
   "emoji_button.symbols": "Símbolos",
   "emoji_button.travel": "Viaxes y llugares",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "¡Equí nun hai barritos!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "Entá nun bloquiesti a nunengún usuariu.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Siguiente",
   "lightbox.previous": "Previous",
-  "lightbox.view_context": "View context",
   "lists.account.add": "Amestar a la llista",
   "lists.account.remove": "Desaniciar de la llista",
   "lists.delete": "Desaniciar la llista",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Change title",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "Títulu nuevu de la llista",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Guetar ente la xente que sigues",
   "lists.subheading": "Les tos llistes",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Menciones",
   "notifications.filter.polls": "Poll results",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} avisos",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json
index 3ffa07a71..4778d6618 100644
--- a/app/javascript/mastodon/locales/bg.json
+++ b/app/javascript/mastodon/locales/bg.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Search results",
   "emoji_button.symbols": "Symbols",
   "emoji_button.travel": "Travel & Places",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Next",
   "lightbox.previous": "Previous",
-  "lightbox.view_context": "View context",
   "lists.account.add": "Add to list",
   "lists.account.remove": "Remove from list",
   "lists.delete": "Delete list",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Change title",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mentions",
   "notifications.filter.polls": "Poll results",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} notifications",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/bn.json b/app/javascript/mastodon/locales/bn.json
index 056a3d424..9b73c3597 100644
--- a/app/javascript/mastodon/locales/bn.json
+++ b/app/javascript/mastodon/locales/bn.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "খোঁজার ফলাফল",
   "emoji_button.symbols": "প্রতীক",
   "emoji_button.travel": "ভ্রমণ এবং স্থান",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "এখানে কোনো টুট নেই!",
   "empty_column.account_unavailable": "নিজস্ব পাতা নেই",
   "empty_column.blocks": "আপনি কোনো ব্যবহারকারীদের ব্লক করেন নি।",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "পরবর্তী",
   "lightbox.previous": "পূর্ববর্তী",
-  "lightbox.view_context": "প্রসঙ্গটি দেখতে",
   "lists.account.add": "তালিকাতে যুক্ত করতে",
   "lists.account.remove": "তালিকা থেকে বাদ দিতে",
   "lists.delete": "তালিকা মুছে ফেলতে",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "শিরোনাম সম্পাদনা করতে",
   "lists.new.create": "তালিকাতে যুক্ত করতে",
   "lists.new.title_placeholder": "তালিকার নতুন শিরোনাম দিতে",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "যাদের অনুসরণ করেন তাদের ভেতরে খুঁজুন",
   "lists.subheading": "আপনার তালিকা",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "উল্লেখিত",
   "notifications.filter.polls": "নির্বাচনের ফলাফল",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} প্রজ্ঞাপন",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/br.json b/app/javascript/mastodon/locales/br.json
index ce7260477..84c5f4523 100644
--- a/app/javascript/mastodon/locales/br.json
+++ b/app/javascript/mastodon/locales/br.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Disoc'hoù an enklask",
   "emoji_button.symbols": "Arouezioù",
   "emoji_button.travel": "Lec'hioù ha Beajoù",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "Toud ebet amañ!",
   "empty_column.account_unavailable": "Profil dihegerz",
   "empty_column.blocks": "N'eus ket bet berzet implijer·ez ganeoc'h c'hoazh.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Da-heul",
   "lightbox.previous": "A-raok",
-  "lightbox.view_context": "Diskouez ar c'hemperzh",
   "lists.account.add": "Ouzhpennañ d'al listenn",
   "lists.account.remove": "Lemel kuit eus al listenn",
   "lists.delete": "Dilemel al listenn",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Cheñch an titl",
   "lists.new.create": "Ouzhpennañ ul listenn",
   "lists.new.title_placeholder": "Titl nevez al listenn",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Search among people you follow",
   "lists.subheading": "Ho listennoù",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Menegoù",
   "notifications.filter.polls": "Disoc'hoù ar sontadegoù",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} a gemennoù",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index 93bcffeaa..13e9e885b 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Resultats de la cerca",
   "emoji_button.symbols": "Símbols",
   "emoji_button.travel": "Viatges i Llocs",
+  "empty_column.account_suspended": "Compte suspès",
   "empty_column.account_timeline": "No hi ha tuts aquí!",
   "empty_column.account_unavailable": "Perfil no disponible",
   "empty_column.blocks": "Encara no has bloquejat cap usuari.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Amplia el quadre de visualització de l’imatge",
   "lightbox.next": "Següent",
   "lightbox.previous": "Anterior",
-  "lightbox.view_context": "Veure el context",
   "lists.account.add": "Afegir a la llista",
   "lists.account.remove": "Treure de la llista",
   "lists.delete": "Esborrar llista",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Canvi de títol",
   "lists.new.create": "Afegir llista",
   "lists.new.title_placeholder": "Nova llista",
-  "lists.replies_policy.all_replies": "Qualsevol usuari seguit",
-  "lists.replies_policy.list_replies": "Membres de la llista",
-  "lists.replies_policy.no_replies": "Ningú",
+  "lists.replies_policy.followed": "Qualsevol usuari seguit",
+  "lists.replies_policy.list": "Membres de la llista",
+  "lists.replies_policy.none": "Ningú",
   "lists.replies_policy.title": "Mostra respostes a:",
   "lists.search": "Cercar entre les persones que segueixes",
   "lists.subheading": "Les teves llistes",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mencions",
   "notifications.filter.polls": "Resultats de l'enquesta",
   "notifications.filter.statuses": "Actualitzacions de gent que segueixes",
+  "notifications.grant_permission": "Concedir permís.",
   "notifications.group": "{count} notificacions",
   "notifications.mark_as_read": "Marca cada notificació com a llegida",
   "notifications.permission_denied": "No s’ha pogut activar les notificacions d’escriptori perquè s’ha denegat el permís.",
   "notifications.permission_denied_alert": "No es poden activar les notificacions del escriptori perquè el permís del navegador ha estat denegat abans",
+  "notifications.permission_required": "Les notificacions d'escriptori no estan disponibles perquè el permís requerit no ha estat concedit.",
   "notifications_permission_banner.enable": "Activar les notificacions d’escriptori",
   "notifications_permission_banner.how_to_control": "Per a rebre notificacions quan Mastodon no està obert cal activar les notificacions d’escriptori. Pots controlar amb precisió quins tipus d’interaccions generen notificacions d’escriptori després d’activar el botó {icon} de dalt.",
   "notifications_permission_banner.title": "Mai et perdis res",
diff --git a/app/javascript/mastodon/locales/co.json b/app/javascript/mastodon/locales/co.json
index 10d5f9008..43e1f6c10 100644
--- a/app/javascript/mastodon/locales/co.json
+++ b/app/javascript/mastodon/locales/co.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Risultati di a cerca",
   "emoji_button.symbols": "Simbuli",
   "emoji_button.travel": "Lochi è Viaghju",
+  "empty_column.account_suspended": "Contu suspesu",
   "empty_column.account_timeline": "Nisun statutu quì!",
   "empty_column.account_unavailable": "Prufile micca dispunibule",
   "empty_column.blocks": "Per avà ùn avete bluccatu manc'un utilizatore.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Ingrandà a finestra d'affissera di i ritratti",
   "lightbox.next": "Siguente",
   "lightbox.previous": "Pricidente",
-  "lightbox.view_context": "Vede u cuntestu",
   "lists.account.add": "Aghjunghje à a lista",
   "lists.account.remove": "Toglie di a lista",
   "lists.delete": "Toglie a lista",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Cambià u titulu",
   "lists.new.create": "Aghjunghje",
   "lists.new.title_placeholder": "Titulu di a lista",
-  "lists.replies_policy.all_replies": "Tutti i vostri abbunamenti",
-  "lists.replies_policy.list_replies": "Membri di a lista",
-  "lists.replies_policy.no_replies": "Nisunu",
+  "lists.replies_policy.followed": "Tutti i vostri abbunamenti",
+  "lists.replies_policy.list": "Membri di a lista",
+  "lists.replies_policy.none": "Nimu",
   "lists.replies_policy.title": "Vede e risposte à:",
   "lists.search": "Circà indè i vostr'abbunamenti",
   "lists.subheading": "E vo liste",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Minzione",
   "notifications.filter.polls": "Risultati di u scandagliu",
   "notifications.filter.statuses": "Messe à ghjornu di e persone chì siguitate",
+  "notifications.grant_permission": "Auturizà.",
   "notifications.group": "{count} nutificazione",
   "notifications.mark_as_read": "Marcà tutte e nutificazione cum'è lette",
-  "notifications.permission_denied": "Ùn si po micca attivà e nutificazione desktop perchè a dumanda d'auturizazione hè stata ricusata",
+  "notifications.permission_denied": "Ùn si po micca attivà e nutificazione desktop perchè l'auturizazione hè stata ricusata",
   "notifications.permission_denied_alert": "Ùn pudete micca attivà e nutificazione nant'à l'urdinatore, perchè avete digià ricusatu a dumanda d'auturizazione di u navigatore",
+  "notifications.permission_required": "Ùn si po micca attivà e nutificazione desktop perchè a l'auturizazione richiesta ùn hè micca stata data.",
   "notifications_permission_banner.enable": "Attivà e nutificazione nant'à l'urdinatore",
   "notifications_permission_banner.how_to_control": "Per riceve nutificazione quandu Mastodon ùn hè micca aperta, attivate e nutificazione nant'à l'urdinatore. Pudete decide quali tippi d'interazione anu da mandà ste nutificazione cù u buttone {icon} quì sopra quandu saranu attivate.",
   "notifications_permission_banner.title": "Ùn mancate mai nunda",
diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json
index c43b3c875..76df857d8 100644
--- a/app/javascript/mastodon/locales/cs.json
+++ b/app/javascript/mastodon/locales/cs.json
@@ -12,12 +12,12 @@
   "account.disable_notifications": "Stop notifying me when @{name} posts",
   "account.domain_blocked": "Doména skryta",
   "account.edit_profile": "Upravit profil",
-  "account.enable_notifications": "Notify me when @{name} posts",
+  "account.enable_notifications": "Oznámit mě na příspěvky @{name}",
   "account.endorse": "Zvýraznit na profilu",
   "account.follow": "Sledovat",
   "account.followers": "Sledující",
   "account.followers.empty": "Tohoto uživatele ještě nikdo nesleduje.",
-  "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
+  "account.followers_counter": "{count, plural, one {{counter} sledující} few {{counter} sledující} many {{counter} sledujících} other {{counter} sledujících}}",
   "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
   "account.follows.empty": "Tento uživatel ještě nikoho nesleduje.",
   "account.follows_you": "Sleduje vás",
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Výsledky hledání",
   "emoji_button.symbols": "Symboly",
   "emoji_button.travel": "Cestování a místa",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "Nejsou tu žádné tooty!",
   "empty_column.account_unavailable": "Profil nedostupný",
   "empty_column.blocks": "Ještě jste nezablokovali žádného uživatele.",
@@ -176,7 +177,7 @@
   "follow_request.authorize": "Autorizovat",
   "follow_request.reject": "Odmítnout",
   "follow_requests.unlocked_explanation": "Přestože váš účet není uzamčen, {domain} si myslí, že budete chtít následující požadavky na sledování zkontrolovat ručně.",
-  "generic.saved": "Saved",
+  "generic.saved": "Uloženo",
   "getting_started.developers": "Vývojáři",
   "getting_started.directory": "Adresář profilů",
   "getting_started.documentation": "Dokumentace",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Další",
   "lightbox.previous": "Předchozí",
-  "lightbox.view_context": "Zobrazit kontext",
   "lists.account.add": "Přidat do seznamu",
   "lists.account.remove": "Odebrat ze seznamu",
   "lists.delete": "Smazat seznam",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Změnit název",
   "lists.new.create": "Přidat seznam",
   "lists.new.title_placeholder": "Název nového seznamu",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Hledejte mezi lidmi, které sledujete",
   "lists.subheading": "Vaše seznamy",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Zmínky",
   "notifications.filter.polls": "Výsledky anket",
   "notifications.filter.statuses": "Aktuality od lidí, které sledujete",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} oznámení",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json
index e5f2e69b0..b3c1947b4 100644
--- a/app/javascript/mastodon/locales/cy.json
+++ b/app/javascript/mastodon/locales/cy.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Canlyniadau chwilio",
   "emoji_button.symbols": "Symbolau",
   "emoji_button.travel": "Teithio & Llefydd",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "Dim tŵtiau fama!",
   "empty_column.account_unavailable": "Proffil ddim ar gael",
   "empty_column.blocks": "Nid ydych wedi blocio unrhyw ddefnyddwyr eto.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Nesaf",
   "lightbox.previous": "Blaenorol",
-  "lightbox.view_context": "Gweld cyd-destyn",
   "lists.account.add": "Ychwanegwch at restr",
   "lists.account.remove": "Dileu o'r rhestr",
   "lists.delete": "Dileu rhestr",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Newid teitl",
   "lists.new.create": "Ychwanegu rhestr",
   "lists.new.title_placeholder": "Teitl rhestr newydd",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Chwilio ymysg pobl yr ydych yn ei ddilyn",
   "lists.subheading": "Eich rhestrau",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Crybwylliadau",
   "notifications.filter.polls": "Canlyniadau pleidlais",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} o hysbysiadau",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json
index 4b312a8f4..7c4b3e4ef 100644
--- a/app/javascript/mastodon/locales/da.json
+++ b/app/javascript/mastodon/locales/da.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Søgeresultater",
   "emoji_button.symbols": "Symboler",
   "emoji_button.travel": "Rejser & steder",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "Ingen bidrag her!",
   "empty_column.account_unavailable": "Profil utilgængelig",
   "empty_column.blocks": "Du har ikke blokeret nogen endnu.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Næste",
   "lightbox.previous": "Forrige",
-  "lightbox.view_context": "Vis kontekst",
   "lists.account.add": "Tilføj til liste",
   "lists.account.remove": "Fjern fra liste",
   "lists.delete": "Slet liste",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Skift titel",
   "lists.new.create": "Tilføj liste",
   "lists.new.title_placeholder": "Ny liste titel",
-  "lists.replies_policy.all_replies": "Enhver fulgt bruger",
-  "lists.replies_policy.list_replies": "Medlemmer af listen",
-  "lists.replies_policy.no_replies": "Ingen",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Vis svar til:",
   "lists.search": "Søg iblandt folk du følger",
   "lists.subheading": "Dine lister",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Statusser der nævner dig",
   "notifications.filter.polls": "Afstemningsresultat",
   "notifications.filter.statuses": "Opdateringer fra personer, du følger",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} notifikationer",
   "notifications.mark_as_read": "Markér alle notifikationer som læst",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Aktivér skrivebordsmeddelelser",
   "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.title": "Gå aldrig glip af noget",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index 3c877308d..1cc962880 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Suchergebnisse",
   "emoji_button.symbols": "Symbole",
   "emoji_button.travel": "Reisen und Orte",
+  "empty_column.account_suspended": "Konto gesperrt",
   "empty_column.account_timeline": "Keine Beiträge!",
   "empty_column.account_unavailable": "Konto nicht verfügbar",
   "empty_column.blocks": "Du hast keine Profile blockiert.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Bildansicht erweitern",
   "lightbox.next": "Weiter",
   "lightbox.previous": "Zurück",
-  "lightbox.view_context": "Beitrag sehen",
   "lists.account.add": "Zur Liste hinzufügen",
   "lists.account.remove": "Von der Liste entfernen",
   "lists.delete": "Liste löschen",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Titel ändern",
   "lists.new.create": "Liste hinzufügen",
   "lists.new.title_placeholder": "Neuer Titel der Liste",
-  "lists.replies_policy.all_replies": "Jeder gefolgte Benutzer",
-  "lists.replies_policy.list_replies": "Mitglieder der Liste",
-  "lists.replies_policy.no_replies": "Niemand",
+  "lists.replies_policy.followed": "Jeder gefolgte Benutzer",
+  "lists.replies_policy.list": "Mitglieder der Liste",
+  "lists.replies_policy.none": "Niemand",
   "lists.replies_policy.title": "Antworten anzeigen für:",
   "lists.search": "Suche nach Leuten denen du folgst",
   "lists.subheading": "Deine Listen",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Erwähnungen",
   "notifications.filter.polls": "Ergebnisse der Umfrage",
   "notifications.filter.statuses": "Updates von Personen, denen du folgst",
+  "notifications.grant_permission": "Zugriff gewährt.",
   "notifications.group": "{count} Benachrichtigungen",
   "notifications.mark_as_read": "Alle Benachrichtigungen als gelesen markieren",
   "notifications.permission_denied": "Desktop-Benachrichtigungen können nicht aktiviert werden, da die Berechtigung verweigert wurde.",
   "notifications.permission_denied_alert": "Desktop-Benachrichtigungen können nicht aktiviert werden, da die Browser-Berechtigung zuvor verweigert wurde",
+  "notifications.permission_required": "Desktop-Benachrichtigungen sind nicht verfügbar, da die erforderliche Berechtigung nicht erteilt wurde.",
   "notifications_permission_banner.enable": "Aktiviere Desktop-Benachrichtigungen",
   "notifications_permission_banner.how_to_control": "Um Benachrichtigungen zu erhalten, wenn Mastodon nicht geöffnet ist, aktiviere die Desktop-Benachrichtigungen. Du kannst genau bestimmen, welche Arten von Interaktionen Desktop-Benachrichtigungen über die {icon} -Taste erzeugen, sobald diese aktiviert sind.",
   "notifications_permission_banner.title": "Verpasse nie etwas",
@@ -379,7 +381,7 @@
   "search_popout.search_format": "Fortgeschrittenes Suchformat",
   "search_popout.tips.full_text": "Einfache Texteingabe gibt Beiträge, die du geschrieben, favorisiert und geteilt hast zurück. Außerdem auch Beiträge in denen du erwähnt wurdest, aber auch passende Nutzernamen, Anzeigenamen oder Hashtags.",
   "search_popout.tips.hashtag": "Hashtag",
-  "search_popout.tips.status": "Beitrag",
+  "search_popout.tips.status": "Tröt",
   "search_popout.tips.text": "Einfache Texteingabe gibt Anzeigenamen, Benutzernamen und Hashtags zurück",
   "search_popout.tips.user": "Nutzer",
   "search_results.accounts": "Personen",
diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json
index 86e4b6140..4ffedfbfb 100644
--- a/app/javascript/mastodon/locales/defaultMessages.json
+++ b/app/javascript/mastodon/locales/defaultMessages.json
@@ -653,6 +653,10 @@
   {
     "descriptors": [
       {
+        "defaultMessage": "Account suspended",
+        "id": "empty_column.account_suspended"
+      },
+      {
         "defaultMessage": "Profile unavailable",
         "id": "empty_column.account_unavailable"
       }
@@ -713,6 +717,10 @@
         "id": "timeline_hint.resources.statuses"
       },
       {
+        "defaultMessage": "Account suspended",
+        "id": "empty_column.account_suspended"
+      },
+      {
         "defaultMessage": "Profile unavailable",
         "id": "empty_column.account_unavailable"
       },
@@ -2161,15 +2169,15 @@
       },
       {
         "defaultMessage": "Any followed user",
-        "id": "lists.replies_policy.all_replies"
+        "id": "lists.replies_policy.followed"
       },
       {
         "defaultMessage": "No one",
-        "id": "lists.replies_policy.no_replies"
+        "id": "lists.replies_policy.none"
       },
       {
         "defaultMessage": "Members of the list",
-        "id": "lists.replies_policy.list_replies"
+        "id": "lists.replies_policy.list"
       },
       {
         "defaultMessage": "Edit list",
@@ -2273,6 +2281,10 @@
         "id": "notifications.permission_denied"
       },
       {
+        "defaultMessage": "Desktop notifications are unavailable because the required permission has not been granted.",
+        "id": "notifications.permission_required"
+      },
+      {
         "defaultMessage": "Quick filter bar",
         "id": "notifications.column_settings.filter_bar.category"
       },
@@ -2356,6 +2368,15 @@
   {
     "descriptors": [
       {
+        "defaultMessage": "Grant permission.",
+        "id": "notifications.grant_permission"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/notifications/components/grant_permission_button.json"
+  },
+  {
+    "descriptors": [
+      {
         "defaultMessage": "{name} favourited your status",
         "id": "notification.favourite"
       },
@@ -2478,6 +2499,10 @@
       {
         "defaultMessage": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
         "id": "confirmations.reply.message"
+      },
+      {
+        "defaultMessage": "Expand this status",
+        "id": "status.open"
       }
     ],
     "path": "app/javascript/mastodon/features/picture_in_picture/components/footer.json"
@@ -2767,15 +2792,6 @@
   {
     "descriptors": [
       {
-        "defaultMessage": "View context",
-        "id": "lightbox.view_context"
-      }
-    ],
-    "path": "app/javascript/mastodon/features/ui/components/audio_modal.json"
-  },
-  {
-    "descriptors": [
-      {
         "defaultMessage": "Are you sure you want to block {name}?",
         "id": "confirmations.block.message"
       },
@@ -3049,10 +3065,6 @@
       {
         "defaultMessage": "Next",
         "id": "lightbox.next"
-      },
-      {
-        "defaultMessage": "View context",
-        "id": "lightbox.view_context"
       }
     ],
     "path": "app/javascript/mastodon/features/ui/components/media_modal.json"
@@ -3221,15 +3233,6 @@
   {
     "descriptors": [
       {
-        "defaultMessage": "View context",
-        "id": "lightbox.view_context"
-      }
-    ],
-    "path": "app/javascript/mastodon/features/ui/components/video_modal.json"
-  },
-  {
-    "descriptors": [
-      {
         "defaultMessage": "Compress image view box",
         "id": "lightbox.compress"
       },
diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json
index bd9f2e7ff..7b7373165 100644
--- a/app/javascript/mastodon/locales/el.json
+++ b/app/javascript/mastodon/locales/el.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Αποτελέσματα αναζήτησης",
   "emoji_button.symbols": "Σύμβολα",
   "emoji_button.travel": "Ταξίδια & Τοποθεσίες",
+  "empty_column.account_suspended": "Λογαριασμός σε αναστολή",
   "empty_column.account_timeline": "Δεν έχει τουτ εδώ!",
   "empty_column.account_unavailable": "Μη διαθέσιμο προφίλ",
   "empty_column.blocks": "Δεν έχεις αποκλείσει κανέναν χρήστη ακόμα.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Ανάπτυξη πλαισίου εμφάνισης εικόνας",
   "lightbox.next": "Επόμενο",
   "lightbox.previous": "Προηγούμενο",
-  "lightbox.view_context": "Εμφάνιση πλαισίου",
   "lists.account.add": "Πρόσθεσε στη λίστα",
   "lists.account.remove": "Βγάλε από τη λίστα",
   "lists.delete": "Διαγραφή λίστας",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Αλλαγή τίτλου",
   "lists.new.create": "Προσθήκη λίστας",
   "lists.new.title_placeholder": "Τίτλος νέας λίστα",
-  "lists.replies_policy.all_replies": "Οποιοσδήποτε χρήστης που ακολουθείς",
-  "lists.replies_policy.list_replies": "Μέλη της λίστας",
-  "lists.replies_policy.no_replies": "Κανείς",
+  "lists.replies_policy.followed": "Οποιοσδήποτε χρήστης που ακολουθείς",
+  "lists.replies_policy.list": "Μέλη της λίστας",
+  "lists.replies_policy.none": "Κανένας",
   "lists.replies_policy.title": "Εμφάνιση απαντήσεων σε:",
   "lists.search": "Αναζήτησε μεταξύ των ανθρώπων που ακουλουθείς",
   "lists.subheading": "Οι λίστες σου",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Αναφορές",
   "notifications.filter.polls": "Αποτελέσματα ψηφοφορίας",
   "notifications.filter.statuses": "Ενημερώσεις από όσους ακολουθείς",
+  "notifications.grant_permission": "Χορήγηση άδειας.",
   "notifications.group": "{count} ειδοποιήσεις",
   "notifications.mark_as_read": "Σημείωσε όλες τις ειδοποιήσεις ως αναγνωσμένες",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "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.title": "Μη χάσετε τίποτα",
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index 6d3224e3d..5bea4e816 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -153,6 +153,7 @@
   "emoji_button.search_results": "Search results",
   "emoji_button.symbols": "Symbols",
   "emoji_button.travel": "Travel & Places",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
@@ -262,7 +263,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Next",
   "lightbox.previous": "Previous",
-  "lightbox.view_context": "View context",
   "lists.account.add": "Add to list",
   "lists.account.remove": "Remove from list",
   "lists.delete": "Delete list",
@@ -270,9 +270,9 @@
   "lists.edit.submit": "Change title",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
@@ -339,10 +339,12 @@
   "notifications.filter.mentions": "Mentions",
   "notifications.filter.polls": "Poll results",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} notifications",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index 1b5f18884..5cff6c5af 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -6,7 +6,7 @@
   "account.block": "Bloki @{name}",
   "account.block_domain": "Bloki {domain}",
   "account.blocked": "Blokita",
-  "account.browse_more_on_origin_server": "Browse more on the original profile",
+  "account.browse_more_on_origin_server": "Rigardi pli al la originala profilo",
   "account.cancel_follow_request": "Nuligi peton de sekvado",
   "account.direct": "Rekte mesaĝi @{name}",
   "account.disable_notifications": "Stop notifying me when @{name} posts",
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Serĉaj rezultoj",
   "emoji_button.symbols": "Simboloj",
   "emoji_button.travel": "Vojaĝoj kaj lokoj",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "Neniu mesaĝo ĉi tie!",
   "empty_column.account_unavailable": "Profilo ne disponebla",
   "empty_column.blocks": "Vi ankoraŭ ne blokis uzanton.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Sekva",
   "lightbox.previous": "Antaŭa",
-  "lightbox.view_context": "Vidi kuntekston",
   "lists.account.add": "Aldoni al la listo",
   "lists.account.remove": "Forigi de la listo",
   "lists.delete": "Forigi la liston",
@@ -266,10 +266,10 @@
   "lists.edit.submit": "Ŝanĝi titolon",
   "lists.new.create": "Aldoni liston",
   "lists.new.title_placeholder": "Titolo de la nova listo",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
-  "lists.replies_policy.title": "Show replies to:",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
+  "lists.replies_policy.title": "Montri respondon al:",
   "lists.search": "Serĉi inter la homoj, kiujn vi sekvas",
   "lists.subheading": "Viaj listoj",
   "load_pending": "{count,plural, one {# nova elemento} other {# novaj elementoj}}",
@@ -277,9 +277,9 @@
   "media_gallery.toggle_visible": "Baskuligi videblecon",
   "missing_indicator.label": "Ne trovita",
   "missing_indicator.sublabel": "Ĉi tiu elemento ne estis trovita",
-  "mute_modal.duration": "Duration",
+  "mute_modal.duration": "Daŭro",
   "mute_modal.hide_notifications": "Ĉu vi volas kaŝi la sciigojn de ĉi tiu uzanto?",
-  "mute_modal.indefinite": "Indefinite",
+  "mute_modal.indefinite": "Nedifinita",
   "navigation_bar.apps": "Telefonaj aplikaĵoj",
   "navigation_bar.blocks": "Blokitaj uzantoj",
   "navigation_bar.bookmarks": "Legosignoj",
@@ -310,7 +310,7 @@
   "notification.own_poll": "Via balotenketo finiĝitis",
   "notification.poll": "Partoprenita balotenketo finiĝis",
   "notification.reblog": "{name} diskonigis vian mesaĝon",
-  "notification.status": "{name} just posted",
+  "notification.status": "{name} ĵus afiŝita",
   "notifications.clear": "Forviŝi sciigojn",
   "notifications.clear_confirmation": "Ĉu vi certas, ke vi volas porĉiame forviŝi ĉiujn viajn sciigojn?",
   "notifications.column_settings.alert": "Retumilaj sciigoj",
@@ -334,13 +334,15 @@
   "notifications.filter.mentions": "Mencioj",
   "notifications.filter.polls": "Balotenketaj rezultoj",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} sciigoj",
-  "notifications.mark_as_read": "Mark every notification as read",
+  "notifications.mark_as_read": "Marki ĉiujn sciigojn legita",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
-  "notifications_permission_banner.enable": "Enable desktop notifications",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
+  "notifications_permission_banner.enable": "Ebligi retumilajn sciigojn",
   "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.title": "Never miss a thing",
+  "notifications_permission_banner.title": "Neniam preterlasas iun ajn",
   "picture_in_picture.restore": "Put it back",
   "poll.closed": "Finita",
   "poll.refresh": "Aktualigi",
@@ -351,13 +353,13 @@
   "poll_button.add_poll": "Aldoni balotenketon",
   "poll_button.remove_poll": "Forigi balotenketon",
   "privacy.change": "Agordi mesaĝan privatecon",
-  "privacy.direct.long": "Afiŝi nur al menciitaj uzantoj",
+  "privacy.direct.long": "Videbla nur al menciitaj uzantoj",
   "privacy.direct.short": "Rekta",
-  "privacy.private.long": "Afiŝi nur al sekvantoj",
-  "privacy.private.short": "Nur por sekvantoj",
-  "privacy.public.long": "Afiŝi en publikaj tempolinioj",
+  "privacy.private.long": "Videbla nur al viaj sekvantoj",
+  "privacy.private.short": "Nur al sekvantoj",
+  "privacy.public.long": "Videbla al ĉiuj, afiŝita en publikaj tempolinioj",
   "privacy.public.short": "Publika",
-  "privacy.unlisted.long": "Ne afiŝi en publikaj tempolinioj",
+  "privacy.unlisted.long": "Videbla al ĉiuj, sed ne en publikaj tempolinioj",
   "privacy.unlisted.short": "Nelistigita",
   "refresh": "Refreŝigu",
   "regeneration_indicator.label": "Ŝargado…",
@@ -448,7 +450,7 @@
   "trends.counter_by_accounts": "{count, plural, one {{counter} persono} other {{counter} personoj}} parolante",
   "trends.trending_now": "Nunaj furoraĵoj",
   "ui.beforeunload": "Via malneto perdiĝos se vi eliras de Mastodon.",
-  "units.short.billion": "{count}B",
+  "units.short.billion": "{count}Md",
   "units.short.million": "{count}M",
   "units.short.thousand": "{count}K",
   "upload_area.title": "Altreni kaj lasi por alŝuti",
diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json
index 57115404b..434382e20 100644
--- a/app/javascript/mastodon/locales/es-AR.json
+++ b/app/javascript/mastodon/locales/es-AR.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Resultados de búsqueda",
   "emoji_button.symbols": "Símbolos",
   "emoji_button.travel": "Viajes y lugares",
+  "empty_column.account_suspended": "Cuenta suspendida",
   "empty_column.account_timeline": "¡No hay toots acá!",
   "empty_column.account_unavailable": "Perfil no disponible",
   "empty_column.blocks": "Todavía no bloqueaste a ningún usuario.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expandir cuadro de vista de imagen",
   "lightbox.next": "Siguiente",
   "lightbox.previous": "Anterior",
-  "lightbox.view_context": "Ver contexto",
   "lists.account.add": "Agregar a lista",
   "lists.account.remove": "Quitar de lista",
   "lists.delete": "Eliminar lista",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Cambiar título",
   "lists.new.create": "Agregar lista",
   "lists.new.title_placeholder": "Nuevo título de lista",
-  "lists.replies_policy.all_replies": "Cualquier cuenta seguida",
-  "lists.replies_policy.list_replies": "Miembros de la lista",
-  "lists.replies_policy.no_replies": "Nadie",
+  "lists.replies_policy.followed": "Cualquier cuenta seguida",
+  "lists.replies_policy.list": "Miembros de la lista",
+  "lists.replies_policy.none": "Nadie",
   "lists.replies_policy.title": "Mostrar respuestas a:",
   "lists.search": "Buscar entre la gente que seguís",
   "lists.subheading": "Tus listas",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Menciones",
   "notifications.filter.polls": "Resultados de encuesta",
   "notifications.filter.statuses": "Actualizaciones de cuentas que seguís",
+  "notifications.grant_permission": "Conceder permiso.",
   "notifications.group": "{count} notificaciones",
   "notifications.mark_as_read": "Marcar cada notificación como leída",
   "notifications.permission_denied": "Las notificaciones de escritorio no están disponibles, debido a una solicitud de permiso del navegador web previamente denegada",
   "notifications.permission_denied_alert": "No se pueden habilitar las notificaciones de escritorio, ya que el permiso del navegador fue denegado antes",
+  "notifications.permission_required": "Las notificaciones de escritorio no están disponibles porque no se concedió el permiso requerido.",
   "notifications_permission_banner.enable": "Habilitar notificaciones de escritorio",
   "notifications_permission_banner.how_to_control": "Para recibir notificaciones cuando Mastodon no está abierto, habilitá las notificaciones de escritorio. Podés controlar con precisión qué tipos de interacciones generan notificaciones de escritorio a través del botón {icon} de arriba, una vez que estén habilitadas.",
   "notifications_permission_banner.title": "No te pierdas nada",
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
index 43c717281..e769d7a48 100644
--- a/app/javascript/mastodon/locales/es.json
+++ b/app/javascript/mastodon/locales/es.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Resultados de búsqueda",
   "emoji_button.symbols": "Símbolos",
   "emoji_button.travel": "Viajes y lugares",
+  "empty_column.account_suspended": "Cuenta suspendida",
   "empty_column.account_timeline": "¡No hay toots aquí!",
   "empty_column.account_unavailable": "Perfil no disponible",
   "empty_column.blocks": "Aún no has bloqueado a ningún usuario.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expandir cuadro de visualización de imagen",
   "lightbox.next": "Siguiente",
   "lightbox.previous": "Anterior",
-  "lightbox.view_context": "Ver contexto",
   "lists.account.add": "Añadir a lista",
   "lists.account.remove": "Quitar de lista",
   "lists.delete": "Borrar lista",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Cambiar título",
   "lists.new.create": "Añadir lista",
   "lists.new.title_placeholder": "Título de la nueva lista",
-  "lists.replies_policy.all_replies": "Cualquier usuario al que sigas",
-  "lists.replies_policy.list_replies": "Miembros de la lista",
-  "lists.replies_policy.no_replies": "Nadie",
+  "lists.replies_policy.followed": "Cualquier usuario seguido",
+  "lists.replies_policy.list": "Miembros de la lista",
+  "lists.replies_policy.none": "Nadie",
   "lists.replies_policy.title": "Mostrar respuestas a:",
   "lists.search": "Buscar entre la gente a la que sigues",
   "lists.subheading": "Tus listas",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Menciones",
   "notifications.filter.polls": "Resultados de la votación",
   "notifications.filter.statuses": "Actualizaciones de gente a la que sigues",
+  "notifications.grant_permission": "Conceder permiso.",
   "notifications.group": "{count} notificaciones",
   "notifications.mark_as_read": "Marcar todas las notificaciones como leídas",
   "notifications.permission_denied": "No se pueden habilitar las notificaciones de escritorio ya que se denegó el permiso.",
   "notifications.permission_denied_alert": "No se pueden habilitar las notificaciones de escritorio, ya que el permiso del navegador fue denegado anteriormente",
+  "notifications.permission_required": "Las notificaciones de escritorio no están disponibles porque no se ha concedido el permiso requerido.",
   "notifications_permission_banner.enable": "Habilitar notificaciones de escritorio",
   "notifications_permission_banner.how_to_control": "Para recibir notificaciones cuando Mastodon no esté abierto, habilite las notificaciones de escritorio. Puedes controlar con precisión qué tipos de interacciones generan notificaciones de escritorio a través del botón {icon} de arriba una vez que estén habilitadas.",
   "notifications_permission_banner.title": "Nunca te pierdas nada",
@@ -390,7 +392,7 @@
   "status.admin_account": "Abrir interfaz de moderación para @{name}",
   "status.admin_status": "Abrir este estado en la interfaz de moderación",
   "status.block": "Bloquear a @{name}",
-  "status.bookmark": "Marcador",
+  "status.bookmark": "Añadir marcador",
   "status.cancel_reblog_private": "Eliminar retoot",
   "status.cannot_reblog": "Este toot no puede retootearse",
   "status.copy": "Copiar enlace al estado",
diff --git a/app/javascript/mastodon/locales/et.json b/app/javascript/mastodon/locales/et.json
index 4e42fb999..54f4159fe 100644
--- a/app/javascript/mastodon/locales/et.json
+++ b/app/javascript/mastodon/locales/et.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Otsitulemused",
   "emoji_button.symbols": "Sümbolid",
   "emoji_button.travel": "Reisimine & Kohad",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "Siin tuute ei ole!",
   "empty_column.account_unavailable": "Profiil pole saadaval",
   "empty_column.blocks": "Sa ei ole veel ühtegi kasutajat blokeerinud.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Järgmine",
   "lightbox.previous": "Eelmine",
-  "lightbox.view_context": "Vaata konteksti",
   "lists.account.add": "Lisa nimistusse",
   "lists.account.remove": "Eemalda nimistust",
   "lists.delete": "Kustuta nimistu",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Muuda pealkiri",
   "lists.new.create": "Lisa nimistu",
   "lists.new.title_placeholder": "Uus nimistu pealkiri",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Otsi Teie poolt jälgitavate inimese hulgast",
   "lists.subheading": "Teie nimistud",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mainimised",
   "notifications.filter.polls": "Küsitluse tulemused",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} teated",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json
index 5139d83aa..80119dc00 100644
--- a/app/javascript/mastodon/locales/eu.json
+++ b/app/javascript/mastodon/locales/eu.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Bilaketaren emaitzak",
   "emoji_button.symbols": "Sinboloak",
   "emoji_button.travel": "Bidaiak eta tokiak",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "Ez dago tootik hemen!",
   "empty_column.account_unavailable": "Profila ez dago eskuragarri",
   "empty_column.blocks": "Ez duzu erabiltzailerik blokeatu oraindik.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Hurrengoa",
   "lightbox.previous": "Aurrekoa",
-  "lightbox.view_context": "Ikusi testuingurua",
   "lists.account.add": "Gehitu zerrendara",
   "lists.account.remove": "Kendu zerrendatik",
   "lists.delete": "Ezabatu zerrenda",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Aldatu izenburua",
   "lists.new.create": "Gehitu zerrenda",
   "lists.new.title_placeholder": "Zerrenda berriaren izena",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Bilatu jarraitzen dituzun pertsonen artean",
   "lists.subheading": "Zure zerrendak",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Aipamenak",
   "notifications.filter.polls": "Inkestaren emaitza",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} jakinarazpen",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json
index 44c936142..b062ec18b 100644
--- a/app/javascript/mastodon/locales/fa.json
+++ b/app/javascript/mastodon/locales/fa.json
@@ -81,7 +81,7 @@
   "column_header.show_settings": "نمایش تنظیمات",
   "column_header.unpin": "رهاکردن",
   "column_subheading.settings": "ساماندهی",
-  "community.column_settings.local_only": "تنها بومی",
+  "community.column_settings.local_only": "فقط محلّی",
   "community.column_settings.media_only": "فقط رسانه",
   "community.column_settings.remote_only": "تنها دوردست",
   "compose_form.direct_message_warning": "این بوق تنها به کاربرانی که از آن‌ها نام برده شده فرستاده خواهد شد.",
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "نتایج جستجو",
   "emoji_button.symbols": "نمادها",
   "emoji_button.travel": "سفر و مکان",
+  "empty_column.account_suspended": "حساب معلق شد",
   "empty_column.account_timeline": "هیچ بوقی این‌جا نیست!",
   "empty_column.account_unavailable": "نمایهٔ موجود نیست",
   "empty_column.blocks": "هنوز کسی را مسدود نکرده‌اید.",
@@ -254,11 +255,10 @@
   "keyboard_shortcuts.unfocus": "برای برداشتن تمرکز از نوشتن/جستجو",
   "keyboard_shortcuts.up": "برای بالا رفتن در فهرست",
   "lightbox.close": "بستن",
-  "lightbox.compress": "Compress image view box",
-  "lightbox.expand": "Expand image view box",
+  "lightbox.compress": "فشرده‌سازی جعبه نمایش تصویر",
+  "lightbox.expand": "گسترش جعبه نمایش تصویر",
   "lightbox.next": "بعدی",
   "lightbox.previous": "قبلی",
-  "lightbox.view_context": "نمایش گفتگو",
   "lists.account.add": "افزودن به فهرست",
   "lists.account.remove": "برداشتن از فهرست",
   "lists.delete": "حذف فهرست",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "تغییر عنوان",
   "lists.new.create": "افزودن فهرست",
   "lists.new.title_placeholder": "عنوان فهرست تازه",
-  "lists.replies_policy.all_replies": "هر کاربر پی‌گیری‌شده",
-  "lists.replies_policy.list_replies": "اعضای فهرست",
-  "lists.replies_policy.no_replies": "هیچ‌کس",
+  "lists.replies_policy.followed": "هر کاربر پیگرفته",
+  "lists.replies_policy.list": "اعضای فهرست",
+  "lists.replies_policy.none": "هیچ کدام",
   "lists.replies_policy.title": "نمایش پاسخ‌ها به:",
   "lists.search": "بین کسانی که پی می‌گیرید بگردید",
   "lists.subheading": "فهرست‌های شما",
@@ -334,12 +334,14 @@
   "notifications.filter.mentions": "نام‌بردن‌ها",
   "notifications.filter.polls": "نتایج نظرسنجی",
   "notifications.filter.statuses": "به‌روز رسانی‌ها از کسانی که پی‌گیرشانید",
+  "notifications.grant_permission": "اعطای مجوز.",
   "notifications.group": "{count} اعلان",
   "notifications.mark_as_read": "نشانه‌گذاری همهٔ آگاهی‌ها به عنوان خوانده‌شده",
   "notifications.permission_denied": "آگاهی‌های میزکار به دلیل رد کردن درخواست اجازهٔ پیشین مرورگر، در دسترس نیستند",
   "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": "اگر می‌خواهید حتی زمانی که ماستودون باز نیست اعلان‌ها نمایش یابند، اعلان‌های میزکار را فعال کنید. به محض فعال شدن از طریق دکمه {icon} بالا می‌توانید به دقت کنترل کنید که چه نوع از تعامل‌ها باعث صادر شدن اعلان‌ها شوند.",
   "notifications_permission_banner.title": "هرگز چیزی را از دست ندهید",
   "picture_in_picture.restore": "برگرداندن",
   "poll.closed": "پایان‌یافته",
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index 21e0290b7..28ff603e9 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Hakutulokset",
   "emoji_button.symbols": "Symbolit",
   "emoji_button.travel": "Matkailu",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "Ei ole 'toots' täällä!",
   "empty_column.account_unavailable": "Profiilia ei löydy",
   "empty_column.blocks": "Et ole vielä estänyt yhtään käyttäjää.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Seuraava",
   "lightbox.previous": "Edellinen",
-  "lightbox.view_context": "Näytä kontekstissa",
   "lists.account.add": "Lisää listaan",
   "lists.account.remove": "Poista listasta",
   "lists.delete": "Poista lista",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Vaihda otsikko",
   "lists.new.create": "Lisää lista",
   "lists.new.title_placeholder": "Uuden listan nimi",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Etsi seuraamistasi henkilöistä",
   "lists.subheading": "Omat listat",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Maininnat",
   "notifications.filter.polls": "Kyselyn tulokset",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} ilmoitusta",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index 5c4b386bc..8eedb8d85 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Résultats de la recherche",
   "emoji_button.symbols": "Symboles",
   "emoji_button.travel": "Lieux & Voyages",
+  "empty_column.account_suspended": "Compte suspendu",
   "empty_column.account_timeline": "Aucun pouet ici !",
   "empty_column.account_unavailable": "Profil non disponible",
   "empty_column.blocks": "Vous n’avez bloqué aucun·e utilisateur·rice pour le moment.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Agrandir la fenêtre de visualisation des images",
   "lightbox.next": "Suivant",
   "lightbox.previous": "Précédent",
-  "lightbox.view_context": "Voir le contexte",
   "lists.account.add": "Ajouter à la liste",
   "lists.account.remove": "Supprimer de la liste",
   "lists.delete": "Supprimer la liste",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Modifier le titre",
   "lists.new.create": "Ajouter une liste",
   "lists.new.title_placeholder": "Titre de la nouvelle liste",
-  "lists.replies_policy.all_replies": "N’importe quel·le utilisateur·rice suivi·e",
-  "lists.replies_policy.list_replies": "Membres de la liste",
-  "lists.replies_policy.no_replies": "Personne",
+  "lists.replies_policy.followed": "N’importe quel·le utilisateur·rice suivi·e",
+  "lists.replies_policy.list": "Membres de la liste",
+  "lists.replies_policy.none": "Personne",
   "lists.replies_policy.title": "Afficher les réponses à :",
   "lists.search": "Rechercher parmi les gens que vous suivez",
   "lists.subheading": "Vos listes",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mentions",
   "notifications.filter.polls": "Résultats des sondages",
   "notifications.filter.statuses": "Mises à jour des personnes que vous suivez",
+  "notifications.grant_permission": "Accorder l’autorisation.",
   "notifications.group": "{count} notifications",
   "notifications.mark_as_read": "Marquer toutes les notifications comme lues",
   "notifications.permission_denied": "Impossible d’activer les notifications de bureau car l’autorisation a été refusée.",
   "notifications.permission_denied_alert": "Les notifications de bureau ne peuvent pas être activées, car l’autorisation du navigateur a été refusée avant",
+  "notifications.permission_required": "Les notifications de bureau ne sont pas disponibles car l’autorisation requise n’a pas été accordée.",
   "notifications_permission_banner.enable": "Activer les notifications de bureau",
   "notifications_permission_banner.how_to_control": "Pour recevoir des notifications lorsque Mastodon n’est pas ouvert, activez les notifications du bureau. Vous pouvez contrôler précisément quels types d’interactions génèrent des notifications de bureau via le bouton {icon} ci-dessus une fois qu’elles sont activées.",
   "notifications_permission_banner.title": "Toujours au courant",
@@ -436,7 +438,7 @@
   "tabs_bar.local_timeline": "Fil public local",
   "tabs_bar.notifications": "Notifications",
   "tabs_bar.search": "Chercher",
-  "time_remaining.days": "{number, plural, one {# day} other {# days}} restants",
+  "time_remaining.days": "{number, plural, one {# jour} other {# jours}} restant·s",
   "time_remaining.hours": "{number, plural, one {# heure} other {# heures}} restantes",
   "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} restantes",
   "time_remaining.moments": "Encore quelques instants",
@@ -448,7 +450,7 @@
   "trends.counter_by_accounts": "{count, plural, one {{counter} personne en parle} other {{counter} personnes en parlent}}",
   "trends.trending_now": "Tendance en ce moment",
   "ui.beforeunload": "Votre brouillon sera perdu si vous quittez Mastodon.",
-  "units.short.billion": "{count}B",
+  "units.short.billion": "{count}Md",
   "units.short.million": "{count}M",
   "units.short.thousand": "{count}K",
   "upload_area.title": "Glissez et déposez pour envoyer",
@@ -468,7 +470,7 @@
   "upload_modal.detect_text": "Détecter le texte de l’image",
   "upload_modal.edit_media": "Modifier le média",
   "upload_modal.hint": "Cliquez ou faites glisser le cercle sur l’aperçu pour choisir le point focal qui sera toujours visible sur toutes les miniatures.",
-  "upload_modal.preparing_ocr": "Préparation de OCR…",
+  "upload_modal.preparing_ocr": "Préparation de l’OCR…",
   "upload_modal.preview_label": "Aperçu ({ratio})",
   "upload_progress.label": "Envoi en cours…",
   "video.close": "Fermer la vidéo",
diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json
index 22230ab73..928742dd7 100644
--- a/app/javascript/mastodon/locales/ga.json
+++ b/app/javascript/mastodon/locales/ga.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Search results",
   "emoji_button.symbols": "Symbols",
   "emoji_button.travel": "Travel & Places",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Next",
   "lightbox.previous": "Previous",
-  "lightbox.view_context": "View context",
   "lists.account.add": "Add to list",
   "lists.account.remove": "Remove from list",
   "lists.delete": "Delete list",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Change title",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mentions",
   "notifications.filter.polls": "Poll results",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} notifications",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json
index 6fe22e1d4..854e31554 100644
--- a/app/javascript/mastodon/locales/gl.json
+++ b/app/javascript/mastodon/locales/gl.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Resultados da procura",
   "emoji_button.symbols": "Símbolos",
   "emoji_button.travel": "Viaxes e Lugares",
+  "empty_column.account_suspended": "Conta suspendida",
   "empty_column.account_timeline": "Non hai toots aquí!",
   "empty_column.account_unavailable": "Perfil non dispoñible",
   "empty_column.blocks": "Aínda non bloqueaches a ningún usuaria.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expandir a caixa de vista da imaxe",
   "lightbox.next": "Seguinte",
   "lightbox.previous": "Anterior",
-  "lightbox.view_context": "Ollar contexto",
   "lists.account.add": "Engadir á listaxe",
   "lists.account.remove": "Eliminar da listaxe",
   "lists.delete": "Eliminar listaxe",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Mudar o título",
   "lists.new.create": "Engadir listaxe",
   "lists.new.title_placeholder": "Título da nova listaxe",
-  "lists.replies_policy.all_replies": "Calquera usuaria á que segues",
-  "lists.replies_policy.list_replies": "Membros da lista",
-  "lists.replies_policy.no_replies": "Ninguén",
+  "lists.replies_policy.followed": "Toda usuaria seguida",
+  "lists.replies_policy.list": "Membros da lista",
+  "lists.replies_policy.none": "Ninguén",
   "lists.replies_policy.title": "Mostrar respostas a:",
   "lists.search": "Procurar entre as persoas que segues",
   "lists.subheading": "As túas listaxes",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mencións",
   "notifications.filter.polls": "Resultados da enquisa",
   "notifications.filter.statuses": "Actualizacións de xente á que segues",
+  "notifications.grant_permission": "Conceder permiso.",
   "notifications.group": "{count} notificacións",
   "notifications.mark_as_read": "Marcar todas as notificacións como lidas",
   "notifications.permission_denied": "Non se activaron as notificacións de escritorio porque se denegou o permiso",
   "notifications.permission_denied_alert": "Non se poden activar as notificacións de escritorio, xa que o permiso para o navegador foi denegado previamente",
+  "notifications.permission_required": "As notificacións de escritorio non están dispoñibles porque non se concedeu o permiso necesario.",
   "notifications_permission_banner.enable": "Activar notificacións de escritorio",
   "notifications_permission_banner.how_to_control": "Activa as notificacións de escritorio para recibir notificacións mentras Mastodon non está aberto. Podes controlar de xeito preciso o tipo de interaccións que crean as notificacións de escritorio a través da {icon} superior unha vez están activadas.",
   "notifications_permission_banner.title": "Non perder nada",
@@ -360,7 +362,7 @@
   "privacy.unlisted.long": "Non publicar nas cronoloxías públicas",
   "privacy.unlisted.short": "Non listado",
   "refresh": "Actualizar",
-  "regeneration_indicator.label": "Cargando…",
+  "regeneration_indicator.label": "Estase a cargar…",
   "regeneration_indicator.sublabel": "Estase a preparar a túa cronoloxía de inicio!",
   "relative_time.days": "{number}d",
   "relative_time.hours": "{number}h",
diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json
index cce1f29e7..123b2e855 100644
--- a/app/javascript/mastodon/locales/he.json
+++ b/app/javascript/mastodon/locales/he.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "תוצאות חיפוש",
   "emoji_button.symbols": "סמלים",
   "emoji_button.travel": "טיולים ואתרים",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "הלאה",
   "lightbox.previous": "הקודם",
-  "lightbox.view_context": "View context",
   "lists.account.add": "Add to list",
   "lists.account.remove": "Remove from list",
   "lists.delete": "Delete list",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Change title",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mentions",
   "notifications.filter.polls": "Poll results",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} notifications",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/hi.json b/app/javascript/mastodon/locales/hi.json
index 275cc3eb9..7e0fe4e7a 100644
--- a/app/javascript/mastodon/locales/hi.json
+++ b/app/javascript/mastodon/locales/hi.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "खोज परिणाम",
   "emoji_button.symbols": "प्रतीक",
   "emoji_button.travel": "यात्रा एवं स्थान",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "सन्नाटा! यहां कोई टूट्स नहीं!",
   "empty_column.account_unavailable": "प्रोफाइल उपलब्ध नहीं",
   "empty_column.blocks": "आप अभी तक किसी भी यूजर के द्वारा ब्लॉक्ड नहीं हो।",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "अगला",
   "lightbox.previous": "पिछला",
-  "lightbox.view_context": "View context",
   "lists.account.add": "Add to list",
   "lists.account.remove": "सूची से निकालें",
   "lists.delete": "सूची हटाएँ",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Change title",
   "lists.new.create": "सूची जोड़ें",
   "lists.new.title_placeholder": "नये सूची का शीर्षक",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Search among people you follow",
   "lists.subheading": "आपकी सूचियाँ",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "उल्लेख",
   "notifications.filter.polls": "चुनाव परिणाम",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} सूचनाएँ",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json
index d59e10a89..f1b78eda4 100644
--- a/app/javascript/mastodon/locales/hr.json
+++ b/app/javascript/mastodon/locales/hr.json
@@ -134,7 +134,7 @@
   "directory.new_arrivals": "New arrivals",
   "directory.recently_active": "Nedavno aktivni",
   "embed.instructions": "Embed this status on your website by copying the code below.",
-  "embed.preview": "Here is what it will look like:",
+  "embed.preview": "Evo kako će izgledati:",
   "emoji_button.activity": "Aktivnost",
   "emoji_button.custom": "Prilagođeno",
   "emoji_button.flags": "Zastave",
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Rezultati pretraživanja",
   "emoji_button.symbols": "Simboli",
   "emoji_button.travel": "Putovanje i mjesta",
+  "empty_column.account_suspended": "Račun je suspendiran",
   "empty_column.account_timeline": "Ovdje nema tootova!",
   "empty_column.account_unavailable": "Profil nije dostupan",
   "empty_column.blocks": "Još niste blokirali nikoga.",
@@ -164,7 +165,7 @@
   "empty_column.home.public_timeline": "javnu vremensku crtu",
   "empty_column.list": "Na ovoj listi još nema ničega. Kada članovi ove liste objave nove tootove, oni će se pojaviti ovdje.",
   "empty_column.lists": "You don't have any lists yet. When you create one, it will show up here.",
-  "empty_column.mutes": "You haven't muted any users yet.",
+  "empty_column.mutes": "Niste utišali nijednog korisnika.",
   "empty_column.notifications": "Još nemate obavijesti. Komunicirajte s drugima kako biste započeli razgovor.",
   "empty_column.public": "Ovdje nema ništa! Napišite nešto javno ili ručno pratite korisnike s drugi poslužitelja da biste ovo popunili",
   "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Sljedeće",
   "lightbox.previous": "Prethodno",
-  "lightbox.view_context": "Prikaži kontekst",
   "lists.account.add": "Dodaj na listu",
   "lists.account.remove": "Ukloni s liste",
   "lists.delete": "Izbriši listu",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Promijeni naslov",
   "lists.new.create": "Dodaj listu",
   "lists.new.title_placeholder": "Naziv nove liste",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Bilo koji praćeni korisnik",
+  "lists.replies_policy.list": "Članovi liste",
+  "lists.replies_policy.none": "Nitko",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Traži među praćenim ljudima",
   "lists.subheading": "Vaše liste",
@@ -277,10 +277,10 @@
   "media_gallery.toggle_visible": "Sakrij {number, plural, one {sliku} other {slike}}",
   "missing_indicator.label": "Nije pronađeno",
   "missing_indicator.sublabel": "This resource could not be found",
-  "mute_modal.duration": "Duration",
+  "mute_modal.duration": "Trajanje",
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "mute_modal.indefinite": "Indefinite",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "Mobilne aplikacije",
   "navigation_bar.blocks": "Blokirani korisnici",
   "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Lokalna vremenska crta",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Spominjanja",
   "notifications.filter.polls": "Rezultati anketa",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} obavijesti",
-  "notifications.mark_as_read": "Mark every notification as read",
+  "notifications.mark_as_read": "Označi sve obavijesti kao pročitane",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
@@ -382,7 +384,7 @@
   "search_popout.tips.status": "toot",
   "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
   "search_popout.tips.user": "korisnik",
-  "search_results.accounts": "People",
+  "search_results.accounts": "Ljudi",
   "search_results.hashtags": "Hashtags",
   "search_results.statuses": "Toots",
   "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.",
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
index 1c8c06284..a0ae7305c 100644
--- a/app/javascript/mastodon/locales/hu.json
+++ b/app/javascript/mastodon/locales/hu.json
@@ -149,6 +149,7 @@
   "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_timeline": "Itt nincs tülkölés!",
   "empty_column.account_unavailable": "A profil nem érhető el",
   "empty_column.blocks": "Még senkit sem tiltottál le.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Képnézet kinagyítása",
   "lightbox.next": "Következő",
   "lightbox.previous": "Előző",
-  "lightbox.view_context": "Környezet megtekintése",
   "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",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Cím megváltoztatása",
   "lists.new.create": "Lista hozzáadása",
   "lists.new.title_placeholder": "Új lista címe",
-  "lists.replies_policy.all_replies": "Bármely követett felhasználó",
-  "lists.replies_policy.list_replies": "A lista tagjai",
-  "lists.replies_policy.no_replies": "Senki",
+  "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.search": "Keresés a követett személyek között",
   "lists.subheading": "Listáid",
@@ -334,10 +334,12 @@
   "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_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",
diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json
index dd904bfca..4d9d0b63e 100644
--- a/app/javascript/mastodon/locales/hy.json
+++ b/app/javascript/mastodon/locales/hy.json
@@ -128,7 +128,7 @@
   "conversation.delete": "Ջնջել խօսակցութիւնը",
   "conversation.mark_as_read": "Նշել որպէս ընթերցուած",
   "conversation.open": "Դիտել խօսակցութիւնը",
-  "conversation.with": "{names}֊երի հետ",
+  "conversation.with": "{names}-ի հետ",
   "directory.federated": "Յայտնի դաշնեզերքից",
   "directory.local": "{domain} տիրոյթից միայն",
   "directory.new_arrivals": "Նորեկներ",
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Որոնման արդիւնքներ",
   "emoji_button.symbols": "Նշաններ",
   "emoji_button.travel": "Ուղեւորութիւն եւ տեղանքներ",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "Այստեղ թթեր չկա՛ն։",
   "empty_column.account_unavailable": "Անձնական էջը հասանելի չի",
   "empty_column.blocks": "Դու դեռ ոչ մէկի չես արգելափակել։",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Հաջորդ",
   "lightbox.previous": "Նախորդ",
-  "lightbox.view_context": "Տեսնել ենթատեքստը",
   "lists.account.add": "Ավելացնել ցանկին",
   "lists.account.remove": "Հանել ցանկից",
   "lists.delete": "Ջնջել ցանկը",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Փոխել վերնագիրը",
   "lists.new.create": "Ավելացնել ցանկ",
   "lists.new.title_placeholder": "Նոր ցանկի վերնագիր",
-  "lists.replies_policy.all_replies": "Ում հետևում եմ",
-  "lists.replies_policy.list_replies": "Ցանկի անդամները",
-  "lists.replies_policy.no_replies": "Ոչ ոք",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "Ոչ ոք",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Փնտրել քո հետեւած մարդկանց մեջ",
   "lists.subheading": "Քո ցանկերը",
@@ -334,14 +334,16 @@
   "notifications.filter.mentions": "Նշումները",
   "notifications.filter.polls": "Հարցման արդիւնքները",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Թոյլատրել։",
   "notifications.group": "{count} ծանուցում",
   "notifications.mark_as_read": "Համարել բոլոր ծանուցումները ընթերցած",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "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.title": "Never miss a thing",
-  "picture_in_picture.restore": "Put it back",
+  "picture_in_picture.restore": "Հետ բերել",
   "poll.closed": "Փակ",
   "poll.refresh": "Թարմացնել",
   "poll.total_people": "{count, plural, one {# հոգի} other {# հոգի}}",
@@ -468,7 +470,7 @@
   "upload_modal.detect_text": "Հայտնբերել տեքստը նկարից",
   "upload_modal.edit_media": "Խմբագրել մեդիան",
   "upload_modal.hint": "Սեղմէք եւ տեղաշարժէք նախադիտման շրջանակը՝ որ ընտրէք մանրապատկերում միշտ տեսանելի կէտը։",
-  "upload_modal.preparing_ocr": "Preparing OCR…",
+  "upload_modal.preparing_ocr": "Գրաճանաչման նախապատրաստում…",
   "upload_modal.preview_label": "Նախադիտում ({ratio})",
   "upload_progress.label": "Վերբեռնվում է…",
   "video.close": "Փակել  տեսագրութիւնը",
diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json
index 562d9d038..24f100b6a 100644
--- a/app/javascript/mastodon/locales/id.json
+++ b/app/javascript/mastodon/locales/id.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Hasil pencarian",
   "emoji_button.symbols": "Simbol",
   "emoji_button.travel": "Tempat Wisata",
+  "empty_column.account_suspended": "Akun ditangguhkan",
   "empty_column.account_timeline": "Tidak ada toot di sini!",
   "empty_column.account_unavailable": "Profil tidak tersedia",
   "empty_column.blocks": "Anda belum memblokir siapapun.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Besarkan kotak tampilan gambar",
   "lightbox.next": "Selanjutnya",
   "lightbox.previous": "Sebelumnya",
-  "lightbox.view_context": "Lihat konteks",
   "lists.account.add": "Tambah ke daftar",
   "lists.account.remove": "Hapus dari daftar",
   "lists.delete": "Hapus daftar",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Ubah judul",
   "lists.new.create": "Tambah daftar",
   "lists.new.title_placeholder": "Judul daftar baru",
-  "lists.replies_policy.all_replies": "Siapapun pengguna yang diikuti",
-  "lists.replies_policy.list_replies": "Anggota di daftar tersebut",
-  "lists.replies_policy.no_replies": "Tidak ada satu pun",
+  "lists.replies_policy.followed": "Siapapun pengguna yang diikuti",
+  "lists.replies_policy.list": "Anggota di daftar tersebut",
+  "lists.replies_policy.none": "Tidak ada satu pun",
   "lists.replies_policy.title": "Tampilkan balasan ke:",
   "lists.search": "Cari di antara orang yang Anda ikuti",
   "lists.subheading": "Daftar Anda",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Sebutan",
   "notifications.filter.polls": "Hasil japat",
   "notifications.filter.statuses": "Pembaruan dari orang yang Anda ikuti",
+  "notifications.grant_permission": "Setujui izin.",
   "notifications.group": "{count} notifikasi",
   "notifications.mark_as_read": "Tandai setiap notifikasi sebagai sudah dibaca",
   "notifications.permission_denied": "Tidak dapat mengaktifkan notifikasi desktop karena izin ditolak.",
   "notifications.permission_denied_alert": "Notifikasi desktop tidak dapat diaktifkan karena izin peramban telah ditolak sebelumnya",
+  "notifications.permission_required": "Notifikasi desktop tidak tersedia karena izin yang dibutuhkan belum disetujui.",
   "notifications_permission_banner.enable": "Aktifkan notifikasi desktop",
   "notifications_permission_banner.how_to_control": "Untuk menerima notifikasi saat Mastodon terbuka, aktifkan notifikasi desktop. Anda dapat mengendalikan tipe interaksi mana yang ditampilkan notifikasi desktop melalui tombol {icon} di atas saat sudah aktif.",
   "notifications_permission_banner.title": "Jangan lewatkan apapun",
diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json
index 1528dec69..998799b9f 100644
--- a/app/javascript/mastodon/locales/io.json
+++ b/app/javascript/mastodon/locales/io.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Search results",
   "emoji_button.symbols": "Symbols",
   "emoji_button.travel": "Travel & Places",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Next",
   "lightbox.previous": "Previous",
-  "lightbox.view_context": "View context",
   "lists.account.add": "Add to list",
   "lists.account.remove": "Remove from list",
   "lists.delete": "Delete list",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Change title",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mentions",
   "notifications.filter.polls": "Poll results",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} notifications",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json
index 43004bebb..d90156835 100644
--- a/app/javascript/mastodon/locales/is.json
+++ b/app/javascript/mastodon/locales/is.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Leitarniðurstöður",
   "emoji_button.symbols": "Tákn",
   "emoji_button.travel": "Ferðalög og staðir",
+  "empty_column.account_suspended": "Notandaaðgangur í bið",
   "empty_column.account_timeline": "Engin tíst hér!",
   "empty_column.account_unavailable": "Notandasnið ekki tiltækt",
   "empty_column.blocks": "Þú hefur ekki ennþá útilokað neina notendur.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Fletta út myndskoðunarreit",
   "lightbox.next": "Næsta",
   "lightbox.previous": "Fyrra",
-  "lightbox.view_context": "Skoða samhengi",
   "lists.account.add": "Bæta á lista",
   "lists.account.remove": "Fjarlægja af lista",
   "lists.delete": "Eyða lista",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Breyta titli",
   "lists.new.create": "Bæta við lista",
   "lists.new.title_placeholder": "Titill á nýjum lista",
-  "lists.replies_policy.all_replies": "Allra notenda sem fylgst er með",
-  "lists.replies_policy.list_replies": "Meðlima listans",
-  "lists.replies_policy.no_replies": "Engra",
+  "lists.replies_policy.followed": "Allra notenda sem fylgst er með",
+  "lists.replies_policy.list": "Meðlima listans",
+  "lists.replies_policy.none": "Engra",
   "lists.replies_policy.title": "Sýna svör til:",
   "lists.search": "Leita meðal þeirra sem þú fylgist með",
   "lists.subheading": "Listarnir þínir",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Tilvísanir",
   "notifications.filter.polls": "Niðurstöður könnunar",
   "notifications.filter.statuses": "Uppfærslur frá fólki sem þú fylgist með",
+  "notifications.grant_permission": "Veita heimild.",
   "notifications.group": "{count} tilkynningar",
   "notifications.mark_as_read": "Merkja allar tilkynningar sem lesnar",
   "notifications.permission_denied": "Tilkynningar á skjáborði eru ekki aðgengilegar megna áður hafnaðra beiðna fyrir vafra",
   "notifications.permission_denied_alert": "Ekki var hægt að virkja tilkynningar á skjáborði, þar sem heimildum fyrir vafra var áður hafnað",
+  "notifications.permission_required": "Tilkynningar á skjáborði eru ekki aðgengilegar þar sem nauðsynlegar heimildir hafa ekki verið veittar.",
   "notifications_permission_banner.enable": "Virkja tilkynningar á skjáborði",
   "notifications_permission_banner.how_to_control": "Til að taka á móti tilkynningum þegar Mastodon er ekki opið, skaltu virkja tilkynningar á skjáborði. Þegar þær eru orðnar virkar geturðu stýrt nákvæmlega hverskonar atvik framleiða tilkynningar með því að nota {icon}-hnappinn hér fyrir ofan.",
   "notifications_permission_banner.title": "Aldrei missa af neinu",
diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json
index c472e098f..b63b231ae 100644
--- a/app/javascript/mastodon/locales/it.json
+++ b/app/javascript/mastodon/locales/it.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Risultati della ricerca",
   "emoji_button.symbols": "Simboli",
   "emoji_button.travel": "Viaggi & Luoghi",
+  "empty_column.account_suspended": "Account sospeso",
   "empty_column.account_timeline": "Nessun toot qui!",
   "empty_column.account_unavailable": "Profilo non disponibile",
   "empty_column.blocks": "Non hai ancora bloccato alcun utente.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Espandi casella di visualizzazione immagine",
   "lightbox.next": "Successivo",
   "lightbox.previous": "Precedente",
-  "lightbox.view_context": "Mostra contesto",
   "lists.account.add": "Aggiungi alla lista",
   "lists.account.remove": "Togli dalla lista",
   "lists.delete": "Elimina lista",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Cambia titolo",
   "lists.new.create": "Aggiungi lista",
   "lists.new.title_placeholder": "Titolo della nuova lista",
-  "lists.replies_policy.all_replies": "Qualsiasi utente seguito",
-  "lists.replies_policy.list_replies": "Iscritti alla lista",
-  "lists.replies_policy.no_replies": "Nessuno",
+  "lists.replies_policy.followed": "Qualsiasi utente seguito",
+  "lists.replies_policy.list": "Iscritti alla lista",
+  "lists.replies_policy.none": "Nessuno",
   "lists.replies_policy.title": "Mostra risposte a:",
   "lists.search": "Cerca tra le persone che segui",
   "lists.subheading": "Le tue liste",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Menzioni",
   "notifications.filter.polls": "Risultati del sondaggio",
   "notifications.filter.statuses": "Aggiornamenti dalle persone che segui",
+  "notifications.grant_permission": "Dai il permesso.",
   "notifications.group": "{count} notifiche",
   "notifications.mark_as_read": "Segna tutte le notifiche come lette",
   "notifications.permission_denied": "Impossibile abilitare le notifiche sul desktop perché il permesso è stato negato.",
   "notifications.permission_denied_alert": "Le notifiche sul desktop non possono essere abilitate, poiché il permesso nel browser è stato negato in precedenza",
+  "notifications.permission_required": "Le notifiche desktop non sono disponibili perché l'autorizzazione richiesta non è stata concessa.",
   "notifications_permission_banner.enable": "Abilita le notifiche sul desktop",
   "notifications_permission_banner.how_to_control": "Per ricevere notifiche quando Mastodon non è aperto, abilita le notifiche desktop. Puoi controllare con precisione quali tipi di interazioni generano notifiche desktop tramite il pulsante {icon} qui sopra, dopo averle abilitate.",
   "notifications_permission_banner.title": "Non perderti mai nulla",
diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json
index a97400bdf..e4334ccc4 100644
--- a/app/javascript/mastodon/locales/ja.json
+++ b/app/javascript/mastodon/locales/ja.json
@@ -153,6 +153,7 @@
   "emoji_button.search_results": "検索結果",
   "emoji_button.symbols": "記号",
   "emoji_button.travel": "旅行と場所",
+  "empty_column.account_suspended": "アカウントは停止されています",
   "empty_column.account_timeline": "トゥートがありません!",
   "empty_column.account_unavailable": "プロフィールは利用できません",
   "empty_column.blocks": "まだ誰もブロックしていません。",
@@ -258,11 +259,10 @@
   "keyboard_shortcuts.unfocus": "トゥート入力欄・検索欄から離れる",
   "keyboard_shortcuts.up": "カラム内一つ上に移動",
   "lightbox.close": "閉じる",
-  "lightbox.compress": "Compress image view box",
-  "lightbox.expand": "Expand image view box",
+  "lightbox.compress": "画像ビューボックスを閉じる",
+  "lightbox.expand": "画像ビューボックスを開く",
   "lightbox.next": "次",
   "lightbox.previous": "前",
-  "lightbox.view_context": "トゥートを表示",
   "lists.account.add": "リストに追加",
   "lists.account.remove": "リストから外す",
   "lists.delete": "リストを削除",
@@ -270,9 +270,9 @@
   "lists.edit.submit": "タイトルを変更",
   "lists.new.create": "リストを作成",
   "lists.new.title_placeholder": "新規リスト名",
-  "lists.replies_policy.all_replies": "フォロー中のユーザー全員",
-  "lists.replies_policy.list_replies": "リストのメンバー",
-  "lists.replies_policy.no_replies": "表示しない",
+  "lists.replies_policy.followed": "フォロー中のユーザー全員",
+  "lists.replies_policy.list": "リストのメンバー",
+  "lists.replies_policy.none": "表示しない",
   "lists.replies_policy.title": "リプライを表示:",
   "lists.search": "フォローしている人の中から検索",
   "lists.subheading": "あなたのリスト",
@@ -339,10 +339,12 @@
   "notifications.filter.mentions": "返信",
   "notifications.filter.polls": "アンケート結果",
   "notifications.filter.statuses": "フォローしている人の新着情報",
+  "notifications.grant_permission": "権限の付与",
   "notifications.group": "{count} 件の通知",
   "notifications.mark_as_read": "すべて既読にする",
   "notifications.permission_denied": "ブラウザの通知が拒否されているためデスクトップ通知は利用できません",
   "notifications.permission_denied_alert": "ブラウザの通知が拒否されているためデスクトップ通知を有効にできません",
+  "notifications.permission_required": "必要な権限が付与されていないため、デスクトップ通知は利用できません。",
   "notifications_permission_banner.enable": "デスクトップ通知を有効にする",
   "notifications_permission_banner.how_to_control": "Mastodon を閉じている間でも通知を受信するにはデスクトップ通知を有効にしてください。有効にすると上の {icon} ボタンから通知の内容を細かくカスタマイズできます。",
   "notifications_permission_banner.title": "お見逃しなく",
diff --git a/app/javascript/mastodon/locales/ka.json b/app/javascript/mastodon/locales/ka.json
index 6a0b47fdc..ad3b1b033 100644
--- a/app/javascript/mastodon/locales/ka.json
+++ b/app/javascript/mastodon/locales/ka.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "ძებნის შედეგები",
   "emoji_button.symbols": "სიმბოლოები",
   "emoji_button.travel": "მოგზაურობა და ადგილები",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "შემდეგი",
   "lightbox.previous": "წინა",
-  "lightbox.view_context": "View context",
   "lists.account.add": "სიაში დამატება",
   "lists.account.remove": "სიიდან ამოშლა",
   "lists.delete": "სიის წაშლა",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Change title",
   "lists.new.create": "სიის დამატება",
   "lists.new.title_placeholder": "ახალი სიის სათაური",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "ძებნა ადამიანებს შორის რომელთაც მიჰყვებით",
   "lists.subheading": "თქვენი სიები",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mentions",
   "notifications.filter.polls": "Poll results",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} შეტყობინება",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/kab.json b/app/javascript/mastodon/locales/kab.json
index 261d003aa..cf9860f1e 100644
--- a/app/javascript/mastodon/locales/kab.json
+++ b/app/javascript/mastodon/locales/kab.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Igemmaḍ n unadi",
   "emoji_button.symbols": "Izamulen",
   "emoji_button.travel": "Imeḍqan d Yinigen",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "Ulac tijewwaqin dagi!",
   "empty_column.account_unavailable": "Ur nufi ara amaɣnu-ayi",
   "empty_column.blocks": "Ur tesḥebseḍ ula yiwen n umseqdac ar tura.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Simeɣer tamnaḍt n uskan n tugna",
   "lightbox.next": "Γer zdat",
   "lightbox.previous": "Γer deffir",
-  "lightbox.view_context": "Ẓer amnaḍ",
   "lists.account.add": "Rnu ɣer tebdart",
   "lists.account.remove": "Kkes seg tebdart",
   "lists.delete": "Kkes tabdart",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Beddel azwel",
   "lists.new.create": "Rnu tabdart",
   "lists.new.title_placeholder": "Azwel amaynut n tebdart",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "Ula yiwen",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Ssken-d tiririyin i:",
   "lists.search": "Nadi gar yemdanen i teṭṭafaṛeḍ",
   "lists.subheading": "Tibdarin-ik·im",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Abdar",
   "notifications.filter.polls": "Igemmaḍ n usenqed",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} n tilɣa",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "D awezɣi ad yili wermad n yilɣa n tnarit axateṛ turagt tettwagdel.",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
@@ -468,7 +470,7 @@
   "upload_modal.detect_text": "Sefru-d aḍris seg tugna",
   "upload_modal.edit_media": "Ẓreg taɣwalt",
   "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.",
-  "upload_modal.preparing_ocr": "Preparing OCR…",
+  "upload_modal.preparing_ocr": "Aheyyi n OCR…",
   "upload_modal.preview_label": "Taskant ({ratio})",
   "upload_progress.label": "Asali iteddu...",
   "video.close": "Mdel tabidyutt",
diff --git a/app/javascript/mastodon/locales/kk.json b/app/javascript/mastodon/locales/kk.json
index a2c0856e5..04a0f9140 100644
--- a/app/javascript/mastodon/locales/kk.json
+++ b/app/javascript/mastodon/locales/kk.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Іздеу нәтижелері",
   "emoji_button.symbols": "Таңбалар",
   "emoji_button.travel": "Саяхат",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "Жазба жоқ ешқандай!",
   "empty_column.account_unavailable": "Профиль қолжетімді емес",
   "empty_column.blocks": "Ешкімді бұғаттамағансыз.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Келесі",
   "lightbox.previous": "Алдыңғы",
-  "lightbox.view_context": "Контексті көрсет",
   "lists.account.add": "Тізімге қосу",
   "lists.account.remove": "Тізімнен шығару",
   "lists.delete": "Тізімді өшіру",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Тақырыбын өзгерту",
   "lists.new.create": "Тізім құру",
   "lists.new.title_placeholder": "Жаңа тізім аты",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Сіз іздеген адамдар арасында іздеу",
   "lists.subheading": "Тізімдеріңіз",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Аталымдар",
   "notifications.filter.polls": "Сауалнама нәтижелері",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} ескертпе",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/kn.json b/app/javascript/mastodon/locales/kn.json
index 0f6c71fb9..a5025bb72 100644
--- a/app/javascript/mastodon/locales/kn.json
+++ b/app/javascript/mastodon/locales/kn.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Search results",
   "emoji_button.symbols": "Symbols",
   "emoji_button.travel": "Travel & Places",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Next",
   "lightbox.previous": "Previous",
-  "lightbox.view_context": "View context",
   "lists.account.add": "Add to list",
   "lists.account.remove": "Remove from list",
   "lists.delete": "Delete list",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Change title",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mentions",
   "notifications.filter.polls": "Poll results",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} notifications",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json
index 24539248a..10f7eabcf 100644
--- a/app/javascript/mastodon/locales/ko.json
+++ b/app/javascript/mastodon/locales/ko.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "검색 결과",
   "emoji_button.symbols": "기호",
   "emoji_button.travel": "여행과 장소",
+  "empty_column.account_suspended": "계정 정지됨",
   "empty_column.account_timeline": "여긴 툿이 없어요!",
   "empty_column.account_unavailable": "프로필 사용 불가",
   "empty_column.blocks": "아직 아무도 차단하지 않았습니다.",
@@ -254,11 +255,10 @@
   "keyboard_shortcuts.unfocus": "작성창에서 포커스 해제",
   "keyboard_shortcuts.up": "리스트에서 위로 이동",
   "lightbox.close": "닫기",
-  "lightbox.compress": "Compress image view box",
-  "lightbox.expand": "Expand image view box",
+  "lightbox.compress": "이미지 박스 압축",
+  "lightbox.expand": "이미지 박스 확장",
   "lightbox.next": "다음",
   "lightbox.previous": "이전",
-  "lightbox.view_context": "게시물 보기",
   "lists.account.add": "리스트에 추가",
   "lists.account.remove": "리스트에서 제거",
   "lists.delete": "리스트 삭제",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "제목 수정",
   "lists.new.create": "리스트 추가",
   "lists.new.title_placeholder": "새 리스트의 이름",
-  "lists.replies_policy.all_replies": "팔로우 한 사용자 누구나",
-  "lists.replies_policy.list_replies": "목록의 멤버들",
-  "lists.replies_policy.no_replies": "아무도 없음",
+  "lists.replies_policy.followed": "팔로우 한 사용자 누구나",
+  "lists.replies_policy.list": "리스트의 구성원들",
+  "lists.replies_policy.none": "아무도 없음",
   "lists.replies_policy.title": "답글 표시:",
   "lists.search": "팔로우 중인 사람들 중에서 찾기",
   "lists.subheading": "당신의 리스트",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "멘션",
   "notifications.filter.polls": "투표 결과",
   "notifications.filter.statuses": "팔로우 하는 사람들의 최신 게시물",
+  "notifications.grant_permission": "권한 부여.",
   "notifications.group": "{count} 개의 알림",
   "notifications.mark_as_read": "모든 알림을 읽은 상태로 표시",
   "notifications.permission_denied": "권한이 거부되었기 때문에 데스크탑 알림을 활성화할 수 없음",
   "notifications.permission_denied_alert": "이전에 브라우저 권한이 거부되었기 때문에, 데스크탑 알림이 활성화 될 수 없습니다.",
+  "notifications.permission_required": "필요한 권한이 승인되지 않아 데스크탑 알림을 사용할 수 없습니다.",
   "notifications_permission_banner.enable": "데스크탑 알림 활성화",
   "notifications_permission_banner.how_to_control": "마스토돈이 열려 있지 않을 때에도 알림을 받으려면, 데스크탑 알림을 활성화 하세요. 당신은 어떤 종류의 반응이 데스크탑 알림을 발생할 지를 {icon} 버튼을 통해 세세하게 설정할 수 있습니다.",
   "notifications_permission_banner.title": "아무것도 놓치지 마세요",
@@ -448,9 +450,9 @@
   "trends.counter_by_accounts": "{counter} 명이 말하는 중",
   "trends.trending_now": "지금 유행중",
   "ui.beforeunload": "지금 나가면 저장되지 않은 항목을 잃게 됩니다.",
-  "units.short.billion": "{count}십억",
-  "units.short.million": "{count}백만",
-  "units.short.thousand": "{count}천",
+  "units.short.billion": "{count}B",
+  "units.short.million": "{count}B",
+  "units.short.thousand": "{count}K",
   "upload_area.title": "드래그 & 드롭으로 업로드",
   "upload_button.label": "미디어 추가 (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_error.limit": "파일 업로드 제한에 도달했습니다.",
diff --git a/app/javascript/mastodon/locales/ku.json b/app/javascript/mastodon/locales/ku.json
index b40a15669..3c359d222 100644
--- a/app/javascript/mastodon/locales/ku.json
+++ b/app/javascript/mastodon/locales/ku.json
@@ -1,5 +1,5 @@
 {
-  "account.account_note_header": "تێبینیتێبینی",
+  "account.account_note_header": "تێبینی    ",
   "account.add_or_remove_from_list": "زیادکردن یان سڕینەوە لە پێرستەکان",
   "account.badges.bot": "بوت",
   "account.badges.group": "گرووپ",
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "ئەنجامەکانی گەڕان",
   "emoji_button.symbols": "هێماکان",
   "emoji_button.travel": "گەشت & شوێنەکان",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "لێرە هیچ توتەک نییە!",
   "empty_column.account_unavailable": "پرۆفایل بەردەست نیە",
   "empty_column.blocks": "تۆ هێشتا هیچ بەکارهێنەرێکت بلۆک نەکردووە.",
@@ -254,11 +255,10 @@
   "keyboard_shortcuts.unfocus": "بۆ دروستکردنی ناوچەی دەق/گەڕان",
   "keyboard_shortcuts.up": "بۆ ئەوەی لە لیستەکەدا بڕۆیت",
   "lightbox.close": "دابخە",
-  "lightbox.compress": "Compress image view box",
-  "lightbox.expand": "Expand image view box",
+  "lightbox.compress": "سندوقی نیشاندانی وێنە بپەستێنە",
+  "lightbox.expand": "فراوانکردنی سندوقی بینینی وێنە",
   "lightbox.next": "داهاتوو",
   "lightbox.previous": "پێشوو",
-  "lightbox.view_context": "پێشاندانی دەق",
   "lists.account.add": "زیادکردن بۆ لیست",
   "lists.account.remove": "لابردن لە لیست",
   "lists.delete": "سڕینەوەی لیست",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "گۆڕینی ناونیشان",
   "lists.new.create": "زیادکردنی لیست",
   "lists.new.title_placeholder": "ناونیشانی لیستی نوێ",
-  "lists.replies_policy.all_replies": "هەربەکارهێنەرێکی شوێنکەوتوو",
-  "lists.replies_policy.list_replies": "ئەندامانی لیستەکە",
-  "lists.replies_policy.no_replies": "هیچکەس",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "پیشاندانی وەڵامەکان بۆ:",
   "lists.search": "بگەڕێ لەناو ئەو کەسانەی کە شوێنیان کەوتویت",
   "lists.subheading": "لیستەکانت",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "ئاماژەکان",
   "notifications.filter.polls": "ئەنجامەکانی ڕاپرسی",
   "notifications.filter.statuses": "نوێکردنەوەکان ئەو کەسانەی کە پەیڕەوی دەکەیت",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} ئاگانامە",
   "notifications.mark_as_read": "هەموو ئاگانامەکان وەک خوێندراوەتەوە نیشان بکە",
   "notifications.permission_denied": "ناتوانرێت ئاگانامەکانی دێسکتۆپ چالاک بکرێت وەک ڕێپێدان ڕەتکرایەوە.",
   "notifications.permission_denied_alert": "ناتوانرێت ئاگانامەکانی دێسکتۆپ چالاک بکرێت، چونکە پێشتر مۆڵەتی وێبگەڕ ڕەتکرایەوە",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "چالاککردنی ئاگانامەکانی دێسکتۆپ",
   "notifications_permission_banner.how_to_control": "بۆ وەرگرتنی ئاگانامەکان کاتێک ماستۆدۆن نەکراوەیە، ئاگانامەکانی دێسکتۆپ چالاک بکە. دەتوانیت بە وردی کۆنترۆڵی جۆری کارلێکەکان بکەیت کە ئاگانامەکانی دێسکتۆپ دروست دەکەن لە ڕێگەی دوگمەی {icon} لەسەرەوە کاتێک چالاک دەکرێن.",
   "notifications_permission_banner.title": "هەرگیز شتێک لە دەست مەدە",
diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json
index 6141f321e..46dc0975d 100644
--- a/app/javascript/mastodon/locales/lt.json
+++ b/app/javascript/mastodon/locales/lt.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Search results",
   "emoji_button.symbols": "Symbols",
   "emoji_button.travel": "Travel & Places",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Next",
   "lightbox.previous": "Previous",
-  "lightbox.view_context": "View context",
   "lists.account.add": "Add to list",
   "lists.account.remove": "Remove from list",
   "lists.delete": "Delete list",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Change title",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mentions",
   "notifications.filter.polls": "Poll results",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} notifications",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json
index 061ab2a83..f075731fe 100644
--- a/app/javascript/mastodon/locales/lv.json
+++ b/app/javascript/mastodon/locales/lv.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Meklēšanas rezultāti",
   "emoji_button.symbols": "Simboli",
   "emoji_button.travel": "Ceļošana & Vietas",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "Šeit ziņojumu nav!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "Tu neesi vēl nevienu bloķējis.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Next",
   "lightbox.previous": "Previous",
-  "lightbox.view_context": "View context",
   "lists.account.add": "Add to list",
   "lists.account.remove": "Remove from list",
   "lists.delete": "Delete list",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Change title",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mentions",
   "notifications.filter.polls": "Poll results",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} notifications",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/mk.json b/app/javascript/mastodon/locales/mk.json
index b640b8c66..6dac40e8b 100644
--- a/app/javascript/mastodon/locales/mk.json
+++ b/app/javascript/mastodon/locales/mk.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Резултати од барање",
   "emoji_button.symbols": "Симболи",
   "emoji_button.travel": "Патувања и Места",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Недостапен профил",
   "empty_column.blocks": "Немате сеуште блокирано корисници.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Next",
   "lightbox.previous": "Previous",
-  "lightbox.view_context": "View context",
   "lists.account.add": "Add to list",
   "lists.account.remove": "Remove from list",
   "lists.delete": "Delete list",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Change title",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Спомнувања",
   "notifications.filter.polls": "Резултати од анкета",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} нотификации",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/ml.json b/app/javascript/mastodon/locales/ml.json
index 08c3cacd0..97b4ec401 100644
--- a/app/javascript/mastodon/locales/ml.json
+++ b/app/javascript/mastodon/locales/ml.json
@@ -2,17 +2,17 @@
   "account.account_note_header": "കുറിപ്പ്",
   "account.add_or_remove_from_list": "പട്ടികയിൽ ചേർക്കുകയോ അല്ലെങ്കിൽ മാറ്റുകയോ ചെയ്യുക",
   "account.badges.bot": "റോബോട്ട്",
-  "account.badges.group": "കൂട്ടം",
-  "account.block": "@{name} നെ ബ്ലോക്ക് ചെയ്യുക",
+  "account.badges.group": "ഗ്രൂപ്പ്",
+  "account.block": "@{name} -നെ തടയുക",
   "account.block_domain": "{domain} ൽ നിന്നുള്ള എല്ലാം മറയ്കുക",
   "account.blocked": "തടഞ്ഞു",
   "account.browse_more_on_origin_server": "യഥാർത്ഥ പ്രൊഫൈലിലേക്ക് പോവുക",
   "account.cancel_follow_request": "പിന്തുടരാനുള്ള അപേക്ഷ നിരസിക്കുക",
   "account.direct": "@{name} ന് നേരിട്ട് മെസേജ് അയക്കുക",
-  "account.disable_notifications": "Stop notifying me when @{name} posts",
+  "account.disable_notifications": "@{name} പോസ്റ്റുചെയ്യുന്നത് എന്നെ അറിയിക്കുന്നത് നിർത്തുക",
   "account.domain_blocked": "മേഖല മറയ്ക്കപ്പെട്ടിരിക്കുന്നു",
   "account.edit_profile": "പ്രൊഫൈൽ തിരുത്തുക",
-  "account.enable_notifications": "Notify me when @{name} posts",
+  "account.enable_notifications": "@{name} പോസ്റ്റ് ചെയ്യുമ്പോൾ എന്നെ അറിയിക്കുക",
   "account.endorse": "പ്രൊഫൈലിൽ പ്രകടമാക്കുക",
   "account.follow": "പിന്തുടരുക",
   "account.followers": "പിന്തുടരുന്നവർ",
@@ -67,7 +67,7 @@
   "column.domain_blocks": "മറയ്ക്കപ്പെട്ട മേഖലകൾ",
   "column.favourites": "പ്രിയപ്പെട്ടവ",
   "column.follow_requests": "പിന്തുടരാനുള്ള അഭ്യർത്ഥനകൾ",
-  "column.home": "ഭവനം",
+  "column.home": "ഹോം",
   "column.lists": "പട്ടികകൾ",
   "column.mutes": "നിശബ്ദമാക്കപ്പെട്ട ഉപയോക്താക്കൾ",
   "column.notifications": "അറിയിപ്പുകൾ",
@@ -105,17 +105,17 @@
   "compose_form.spoiler.unmarked": "എഴുത്ത് മറയ്ക്കപ്പെട്ടിട്ടില്ല",
   "compose_form.spoiler_placeholder": "നിങ്ങളുടെ മുന്നറിയിപ്പ് ഇവിടെ എഴുതുക",
   "confirmation_modal.cancel": "റദ്ദാക്കുക",
-  "confirmations.block.block_and_report": "Block & Report",
+  "confirmations.block.block_and_report": "തടയുകയും റിപ്പോർട്ടും ചെയ്യുക",
   "confirmations.block.confirm": "തടയുക",
-  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.block.message": "{name} തടയാൻ നിങ്ങൾ ആഗ്രഹിക്കുന്നുണ്ടോ?",
   "confirmations.delete.confirm": "മായ്ക്കുക",
-  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.delete.message": "ഈ ടൂട്ട് ഇല്ലാതാക്കണം എന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ?",
   "confirmations.delete_list.confirm": "മായ്ക്കുക",
-  "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
-  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.delete_list.message": "ഈ പട്ടിക എന്നെന്നേക്കുമായി നീക്കം ചെയ്യാൻ നിങ്ങൾ ആഗ്രഹിക്കുന്നുണ്ടോ?",
+  "confirmations.domain_block.confirm": "മുഴുവൻ ഡൊമെയ്‌നും തടയുക",
   "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.",
   "confirmations.logout.confirm": "പുറത്തുകടക്കുക",
-  "confirmations.logout.message": "Are you sure you want to log out?",
+  "confirmations.logout.message": "നിങ്ങൾക്ക് ലോഗ് ഔട്ട് ചെയ്യണമെന്ന് ഉറപ്പാണോ?",
   "confirmations.mute.confirm": "നിശ്ശബ്ദമാക്കുക",
   "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.",
   "confirmations.mute.message": "Are you sure you want to mute {name}?",
@@ -133,7 +133,7 @@
   "directory.local": "{domain} ൽ നിന്ന് മാത്രം",
   "directory.new_arrivals": "പുതിയ വരവുകൾ",
   "directory.recently_active": "അടുത്തിടെയായി സജീവമായ",
-  "embed.instructions": "Embed this status on your website by copying the code below.",
+  "embed.instructions": "ചുവടെയുള്ള കോഡ് പകർത്തിക്കൊണ്ട് നിങ്ങളുടെ വെബ്‌സൈറ്റിൽ ഈ ടൂട്ട് ഉൾച്ചേർക്കുക.",
   "embed.preview": "ഇത് ഇങ്ങനെ കാണപ്പെടും:",
   "emoji_button.activity": "പ്രവര്‍ത്തനം",
   "emoji_button.custom": "സ്വന്തമായ ഭേദഗതി",
@@ -141,7 +141,7 @@
   "emoji_button.food": "ഭക്ഷണവും പാനീയവും",
   "emoji_button.label": "ഇമോജി ചേർക്കുക",
   "emoji_button.nature": "പ്രകൃതി",
-  "emoji_button.not_found": "എമോജി പാടില്ല (╯°□°)╯︵ ┻━┻",
+  "emoji_button.not_found": "ഇമോജി പാടില്ല (╯°□°)╯︵ ┻━┻",
   "emoji_button.objects": "വസ്തുക്കൾ",
   "emoji_button.people": "ആളുകൾ",
   "emoji_button.recent": "അടിക്കടി ഉപയോഗിക്കുന്നവ",
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "തിരച്ചിൽ ഫലങ്ങൾ",
   "emoji_button.symbols": "ചിഹ്നങ്ങൾ",
   "emoji_button.travel": "യാത്രയും സ്ഥലങ്ങളും",
+  "empty_column.account_suspended": "അക്കൗണ്ട് താൽക്കാലികമായി നിർത്തിവച്ചു",
   "empty_column.account_timeline": "ഇവിടെ ടൂട്ടുകൾ ഇല്ല!",
   "empty_column.account_unavailable": "പ്രൊഫൈൽ ലഭ്യമല്ല",
   "empty_column.blocks": "നിങ്ങൾ ഇതുവരെ ഒരു ഉപയോക്താക്കളെയും തടഞ്ഞിട്ടില്ല.",
@@ -159,7 +160,7 @@
   "empty_column.favourited_statuses": "നിങ്ങൾക്ക് ഇത് വരെ ഒരു പ്രിയപ്പെട്ട ടൂട്ടും ഇല്ല. നിങ്ങൾ അങ്ങനെ ഒന്ന് പ്രിയപ്പെടുന്ന പക്ഷം അതിവിടെ കാണപ്പെടുന്നതാണ്.",
   "empty_column.favourites": "ഇതുവരെ ആരും ഈ ടൂട്ട് പ്രിയപ്പെട്ടതായി അടയാളപ്പെടുത്തിയിട്ടില്ല. ആരെങ്കിലും അങ്ങനെ ചെയ്യുന്നപക്ഷം അതിവിടെ കാണപ്പെടുന്നതാണ്.",
   "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
-  "empty_column.hashtag": "There is nothing in this hashtag yet.",
+  "empty_column.hashtag": "ഈ ഹാഷ്‌ടാഗിൽ ഇതുവരെ ഒന്നുമില്ല.",
   "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
   "empty_column.home.public_timeline": "പൊതു സമയരേഖ",
   "empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.",
@@ -178,7 +179,7 @@
   "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.",
   "generic.saved": "സംരക്ഷിച്ചു",
   "getting_started.developers": "വികസിപ്പിക്കുന്നവർ",
-  "getting_started.directory": "രൂപരേഖ നാമഗൃഹസൂചി",
+  "getting_started.directory": "പ്രൊഫൈൽ ഡയറക്ടറി",
   "getting_started.documentation": "രേഖാ സമാഹരണം",
   "getting_started.heading": "തുടക്കം കുറിക്കുക",
   "getting_started.invite": "ആളുകളെ ക്ഷണിക്കുക",
@@ -189,7 +190,7 @@
   "hashtag.column_header.tag_mode.any": "അല്ലെങ്കിൽ {additional}",
   "hashtag.column_header.tag_mode.none": "{additional} ഇല്ലാതെ",
   "hashtag.column_settings.select.no_options_message": "ഒരു സൂചനയും കണ്ടെത്തിയില്ല",
-  "hashtag.column_settings.select.placeholder": "ചർച്ചാവിഷയങ്ങൾ എഴുതുക…",
+  "hashtag.column_settings.select.placeholder": "ഹാഷ്ടാഗുകൾ എഴുതുക…",
   "hashtag.column_settings.tag_mode.all": "ഇവയെല്ലാം",
   "hashtag.column_settings.tag_mode.any": "ഇവയിലേതെങ്കിലും",
   "hashtag.column_settings.tag_mode.none": "ഇതിലൊന്നുമല്ല",
@@ -205,7 +206,7 @@
   "introduction.federation.action": "അടുത്തത്",
   "introduction.federation.federated.headline": "സംയുക്തമാക്കിയ",
   "introduction.federation.federated.text": "Public posts from other servers of the fediverse will appear in the federated timeline.",
-  "introduction.federation.home.headline": "ഭവനം",
+  "introduction.federation.home.headline": "ഹോം",
   "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!",
   "introduction.federation.local.headline": "പ്രാദേശികം",
   "introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
@@ -232,17 +233,17 @@
   "keyboard_shortcuts.favourites": "to open favourites list",
   "keyboard_shortcuts.federated": "to open federated timeline",
   "keyboard_shortcuts.heading": "കീബോർഡ് എളുപ്പവഴികൾ",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "ഹോം ടൈംലൈൻ തുറക്കുന്നതിന്",
   "keyboard_shortcuts.hotkey": "Hotkey",
   "keyboard_shortcuts.legend": "to display this legend",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "പ്രാദേശിക സമയരേഖ തുറക്കാൻ",
   "keyboard_shortcuts.mention": "to mention author",
   "keyboard_shortcuts.muted": "to open muted users list",
-  "keyboard_shortcuts.my_profile": "to open your profile",
+  "keyboard_shortcuts.my_profile": "നിങ്ങളുടെ പ്രൊഫൈൽ തുറക്കാൻ",
   "keyboard_shortcuts.notifications": "to open notifications column",
-  "keyboard_shortcuts.open_media": "to open media",
+  "keyboard_shortcuts.open_media": "മീഡിയ തുറക്കാൻ",
   "keyboard_shortcuts.pinned": "to open pinned toots list",
-  "keyboard_shortcuts.profile": "to open author's profile",
+  "keyboard_shortcuts.profile": "രചയിതാവിന്റെ പ്രൊഫൈൽ തുറക്കുന്നതിന്",
   "keyboard_shortcuts.reply": "മറുപടി അയക്കാൻ",
   "keyboard_shortcuts.requests": "to open follow requests list",
   "keyboard_shortcuts.search": "to focus search",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "അടുത്തത്",
   "lightbox.previous": "പുറകോട്ട്",
-  "lightbox.view_context": "View context",
   "lists.account.add": "പട്ടികയിലേക്ക് ചേർക്കുക",
   "lists.account.remove": "പട്ടികയിൽ നിന്ന് ഒഴിവാക്കുക",
   "lists.delete": "പട്ടിക ഒഴിവാക്കുക",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "തലക്കെട്ട് മാറ്റുക",
   "lists.new.create": "പുതിയ പട്ടിക ചേർക്കുക",
   "lists.new.title_placeholder": "New list title",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "ആരുമില്ല",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Search among people you follow",
   "lists.subheading": "എന്റെ പട്ടികകൾ",
@@ -277,206 +277,208 @@
   "media_gallery.toggle_visible": "Hide {number, plural, one {image} other {images}}",
   "missing_indicator.label": "കാണാനില്ല",
   "missing_indicator.sublabel": "This resource could not be found",
-  "mute_modal.duration": "Duration",
+  "mute_modal.duration": "കാലാവധി",
   "mute_modal.hide_notifications": "Hide notifications from this user?",
-  "mute_modal.indefinite": "Indefinite",
-  "navigation_bar.apps": "Mobile apps",
-  "navigation_bar.blocks": "Blocked users",
-  "navigation_bar.bookmarks": "അടയാളങ്ങൾ",
-  "navigation_bar.community_timeline": "Local timeline",
+  "mute_modal.indefinite": "അനിശ്ചിതകാല",
+  "navigation_bar.apps": "മൊബൈൽ ആപ്പുകൾ",
+  "navigation_bar.blocks": "തടയപ്പെട്ട ഉപയോക്താക്കൾ",
+  "navigation_bar.bookmarks": "ബുക്ക്മാർക്കുകൾ",
+  "navigation_bar.community_timeline": "പ്രാദേശിക സമയരേഖ",
   "navigation_bar.compose": "പുതിയ ടൂട്ട് എഴുതുക",
-  "navigation_bar.direct": "Direct messages",
-  "navigation_bar.discover": "Discover",
+  "navigation_bar.direct": "നേരിട്ടുള്ള സന്ദേശങ്ങൾ",
+  "navigation_bar.discover": "കണ്ടെത്തുക",
   "navigation_bar.domain_blocks": "Hidden domains",
-  "navigation_bar.edit_profile": "Edit profile",
-  "navigation_bar.favourites": "Favourites",
+  "navigation_bar.edit_profile": "പ്രൊഫൈൽ തിരുത്തുക",
+  "navigation_bar.favourites": "പ്രിയപ്പെട്ടവ",
   "navigation_bar.filters": "Muted words",
-  "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.follow_requests": "പിന്തുടരാനുള്ള അഭ്യർത്ഥനകൾ",
   "navigation_bar.follows_and_followers": "Follows and followers",
-  "navigation_bar.info": "About this server",
+  "navigation_bar.info": "ഈ സെർവറിനെക്കുറിച്ച്",
   "navigation_bar.keyboard_shortcuts": "Hotkeys",
-  "navigation_bar.lists": "Lists",
-  "navigation_bar.logout": "Logout",
-  "navigation_bar.mutes": "Muted users",
+  "navigation_bar.lists": "ലിസ്റ്റുകൾ",
+  "navigation_bar.logout": "ലോഗൗട്ട്",
+  "navigation_bar.mutes": "നിശബ്ദമാക്കപ്പെട്ട ഉപയോക്താക്കൾ",
   "navigation_bar.personal": "Personal",
   "navigation_bar.pins": "Pinned toots",
-  "navigation_bar.preferences": "Preferences",
+  "navigation_bar.preferences": "ക്രമീകരണങ്ങൾ",
   "navigation_bar.public_timeline": "Federated timeline",
-  "navigation_bar.security": "Security",
+  "navigation_bar.security": "സുരക്ഷ",
   "notification.favourite": "{name} favourited your status",
-  "notification.follow": "{name} followed you",
+  "notification.follow": "{name} നിങ്ങളെ പിന്തുടർന്നു",
   "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} mentioned you",
-  "notification.own_poll": "Your poll has ended",
+  "notification.own_poll": "നിങ്ങളുടെ പോൾ അവസാനിച്ചു",
   "notification.poll": "A poll you have voted in has ended",
-  "notification.reblog": "{name} boosted your status",
-  "notification.status": "{name} just posted",
-  "notifications.clear": "Clear notifications",
+  "notification.reblog": "{name} നിങ്ങളുടെ പോസ്റ്റ് ബൂസ്റ്റ് ചെയ്തു",
+  "notification.status": "{name} ഇപ്പോൾ പോസ്റ്റുചെയ്‌തു",
+  "notifications.clear": "അറിയിപ്പ് മായ്ക്കുക",
   "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
-  "notifications.column_settings.alert": "Desktop notifications",
-  "notifications.column_settings.favourite": "Favourites:",
-  "notifications.column_settings.filter_bar.advanced": "Display all categories",
+  "notifications.column_settings.alert": "ഡെസ്ക്ടോപ്പ് അറിയിപ്പുകൾ",
+  "notifications.column_settings.favourite": "പ്രിയപ്പെട്ടവ:",
+  "notifications.column_settings.filter_bar.advanced": "എല്ലാ വിഭാഗങ്ങളും പ്രദർശിപ്പിക്കുക",
   "notifications.column_settings.filter_bar.category": "Quick filter bar",
-  "notifications.column_settings.filter_bar.show": "Show",
+  "notifications.column_settings.filter_bar.show": "കാണിക്കുക",
   "notifications.column_settings.follow": "New followers:",
-  "notifications.column_settings.follow_request": "New follow requests:",
+  "notifications.column_settings.follow_request": "പുതിയ പിന്തുടരൽ അഭ്യർത്ഥനകൾ:",
   "notifications.column_settings.mention": "Mentions:",
-  "notifications.column_settings.poll": "Poll results:",
+  "notifications.column_settings.poll": "പോൾ ഫലങ്ങൾ:",
   "notifications.column_settings.push": "Push notifications",
-  "notifications.column_settings.reblog": "Boosts:",
+  "notifications.column_settings.reblog": "ബൂസ്റ്റുകൾ:",
   "notifications.column_settings.show": "Show in column",
-  "notifications.column_settings.sound": "Play sound",
-  "notifications.column_settings.status": "New toots:",
-  "notifications.filter.all": "All",
-  "notifications.filter.boosts": "Boosts",
-  "notifications.filter.favourites": "Favourites",
-  "notifications.filter.follows": "Follows",
+  "notifications.column_settings.sound": "ശബ്ദം പ്ലേ ചെയ്യുക",
+  "notifications.column_settings.status": "പുതിയ ടൂട്ടുകൾ:",
+  "notifications.filter.all": "എല്ലാം",
+  "notifications.filter.boosts": "ബൂസ്റ്റുകൾ",
+  "notifications.filter.favourites": "പ്രിയപ്പെട്ടവ",
+  "notifications.filter.follows": "പിന്തുടരുന്നു",
   "notifications.filter.mentions": "Mentions",
-  "notifications.filter.polls": "Poll results",
-  "notifications.filter.statuses": "Updates from people you follow",
-  "notifications.group": "{count} notifications",
-  "notifications.mark_as_read": "Mark every notification as read",
+  "notifications.filter.polls": "പോൾ ഫലങ്ങൾ",
+  "notifications.filter.statuses": "നിങ്ങൾ പിന്തുടരുന്ന ആളുകളിൽ നിന്നുള്ള അപ്‌ഡേറ്റുകൾ",
+  "notifications.grant_permission": "അനുമതി നൽകുക.",
+  "notifications.group": "{count} അറിയിപ്പുകൾ",
+  "notifications.mark_as_read": "എല്ലാ അറിയിപ്പുകളും വായിച്ചതായി അടയാളപ്പെടുത്തുക",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
-  "notifications_permission_banner.enable": "Enable desktop notifications",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
+  "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.title": "Never miss a thing",
-  "picture_in_picture.restore": "Put it back",
-  "poll.closed": "Closed",
-  "poll.refresh": "Refresh",
+  "picture_in_picture.restore": "തിരികെ വയ്ക്കുക",
+  "poll.closed": "അടച്ചു",
+  "poll.refresh": "പുതുക്കുക",
   "poll.total_people": "{count, plural, one {# person} other {# people}}",
   "poll.total_votes": "{count, plural, one {# vote} other {# votes}}",
-  "poll.vote": "Vote",
+  "poll.vote": "വോട്ട് ചെയ്യുക",
   "poll.voted": "You voted for this answer",
-  "poll_button.add_poll": "Add a poll",
-  "poll_button.remove_poll": "Remove poll",
+  "poll_button.add_poll": "ഒരു പോൾ ചേർക്കുക",
+  "poll_button.remove_poll": "പോൾ നീക്കംചെയ്യുക",
   "privacy.change": "Adjust status privacy",
   "privacy.direct.long": "Post to mentioned users only",
-  "privacy.direct.short": "Direct",
+  "privacy.direct.short": "നേരിട്ട്",
   "privacy.private.long": "Post to followers only",
   "privacy.private.short": "Followers-only",
   "privacy.public.long": "Post to public timelines",
   "privacy.public.short": "Public",
   "privacy.unlisted.long": "Do not show in public timelines",
   "privacy.unlisted.short": "Unlisted",
-  "refresh": "Refresh",
-  "regeneration_indicator.label": "Loading…",
-  "regeneration_indicator.sublabel": "Your home feed is being prepared!",
+  "refresh": "പുതുക്കുക",
+  "regeneration_indicator.label": "ലഭ്യമാക്കുന്നു…",
+  "regeneration_indicator.sublabel": "നിങ്ങളുടെ ഹോം ഫീഡ് തയാറാക്കുന്നു!",
   "relative_time.days": "{number}d",
   "relative_time.hours": "{number}h",
-  "relative_time.just_now": "now",
+  "relative_time.just_now": "ഇപ്പോൾ",
   "relative_time.minutes": "{number}m",
   "relative_time.seconds": "{number}s",
-  "relative_time.today": "today",
-  "reply_indicator.cancel": "Cancel",
+  "relative_time.today": "ഇന്ന്",
+  "reply_indicator.cancel": "റദ്ദാക്കുക",
   "report.forward": "Forward to {target}",
   "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
   "report.hint": "The report will be sent to your server moderators. You can provide an explanation of why you are reporting this account below:",
-  "report.placeholder": "Additional comments",
-  "report.submit": "Submit",
+  "report.placeholder": "കൂടുതൽ അഭിപ്രായങ്ങൾ",
+  "report.submit": "സമർപ്പിക്കുക",
   "report.target": "Report {target}",
-  "search.placeholder": "Search",
+  "search.placeholder": "തിരയുക",
   "search_popout.search_format": "Advanced search format",
   "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": "hashtag",
-  "search_popout.tips.status": "status",
+  "search_popout.tips.hashtag": "ഹാഷ്ടാഗ്",
+  "search_popout.tips.status": "ടൂട്ട്",
   "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
-  "search_popout.tips.user": "user",
-  "search_results.accounts": "People",
-  "search_results.hashtags": "Hashtags",
-  "search_results.statuses": "Toots",
+  "search_popout.tips.user": "ഉപയോക്താവ്",
+  "search_results.accounts": "ആളുകൾ",
+  "search_results.hashtags": "ഹാഷ്ടാഗുകൾ",
+  "search_results.statuses": "ടൂട്ടുകൾ",
   "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.",
   "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
-  "status.block": "Block @{name}",
-  "status.bookmark": "Bookmark",
+  "status.block": "@{name} -നെ തടയുക",
+  "status.bookmark": "ബുക്ക്മാർക്ക്",
   "status.cancel_reblog_private": "Unboost",
-  "status.cannot_reblog": "This post cannot be boosted",
-  "status.copy": "Copy link to status",
+  "status.cannot_reblog": "ഈ പോസ്റ്റ് ബൂസ്റ്ചെയ്യാൻ കഴിയില്ല",
+  "status.copy": "ടൂട്ടിലേക്ക് ലിങ്ക് പകർത്തുക",
   "status.delete": "മായ്ക്കുക",
   "status.detailed_status": "വിശദമായ സംഭാഷണ കാഴ്‌ച",
   "status.direct": "@{name} ന് നേരിട്ട് മെസേജ് അയക്കുക",
   "status.embed": "ഉൾച്ചേർക്കുക",
   "status.favourite": "പ്രിയപ്പെട്ടത്",
-  "status.filtered": "Filtered",
+  "status.filtered": "ഫിൽട്ടർ ചെയ്‌തു",
   "status.load_more": "കൂടുതൽ ലോഡു ചെയ്യുക",
-  "status.media_hidden": "Media hidden",
+  "status.media_hidden": "മീഡിയ മറച്ചു",
   "status.mention": "@{name} സൂചിപ്പിക്കുക",
-  "status.more": "More",
-  "status.mute": "Mute @{name}",
+  "status.more": "കൂടുതൽ",
+  "status.mute": "@{name}-നെ നിശ്ശബ്ദമാക്കുക",
   "status.mute_conversation": "Mute conversation",
   "status.open": "Expand this status",
   "status.pin": "Pin on profile",
   "status.pinned": "Pinned toot",
-  "status.read_more": "Read more",
-  "status.reblog": "Boost",
+  "status.read_more": "കൂടുതൽ വായിക്കുക",
+  "status.reblog": "ബൂസ്റ്റ്",
   "status.reblog_private": "Boost with original visibility",
-  "status.reblogged_by": "{name} boosted",
+  "status.reblogged_by": "{name} ബൂസ്റ്റ് ചെയ്തു",
   "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
-  "status.redraft": "Delete & re-draft",
-  "status.remove_bookmark": "Remove bookmark",
-  "status.reply": "Reply",
+  "status.redraft": "ഇല്ലാതാക്കുക & വീണ്ടും ഡ്രാഫ്റ്റ് ചെയ്യുക",
+  "status.remove_bookmark": "ബുക്ക്മാർക്ക് നീക്കംചെയ്യുക",
+  "status.reply": "മറുപടി",
   "status.replyAll": "Reply to thread",
   "status.report": "Report @{name}",
   "status.sensitive_warning": "Sensitive content",
-  "status.share": "Share",
-  "status.show_less": "Show less",
+  "status.share": "പങ്കിടുക",
+  "status.show_less": "കുറച്ച് കാണിക്കുക",
   "status.show_less_all": "Show less for all",
-  "status.show_more": "Show more",
-  "status.show_more_all": "Show more for all",
+  "status.show_more": "കൂടുതകൽ കാണിക്കുക",
+  "status.show_more_all": "എല്ലാവർക്കുമായി കൂടുതൽ കാണിക്കുക",
   "status.show_thread": "Show thread",
-  "status.uncached_media_warning": "Not available",
+  "status.uncached_media_warning": "ലഭ്യമല്ല",
   "status.unmute_conversation": "Unmute conversation",
   "status.unpin": "Unpin from profile",
   "suggestions.dismiss": "Dismiss suggestion",
-  "suggestions.header": "You might be interested in…",
-  "tabs_bar.federated_timeline": "Federated",
-  "tabs_bar.home": "Home",
-  "tabs_bar.local_timeline": "Local",
-  "tabs_bar.notifications": "Notifications",
-  "tabs_bar.search": "Search",
+  "suggestions.header": "നിങ്ങൾക്ക് താൽപ്പര്യമുണ്ടാകാം…",
+  "tabs_bar.federated_timeline": "സംയുക്തമാക്കിയ",
+  "tabs_bar.home": "ഹോം",
+  "tabs_bar.local_timeline": "പ്രാദേശികം",
+  "tabs_bar.notifications": "അറിയിപ്പുകൾ",
+  "tabs_bar.search": "തിരയുക",
   "time_remaining.days": "{number, plural, one {# day} other {# days}} left",
   "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left",
   "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left",
   "time_remaining.moments": "Moments remaining",
   "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left",
   "timeline_hint.remote_resource_not_displayed": "{resource} from other servers are not displayed.",
-  "timeline_hint.resources.followers": "Followers",
-  "timeline_hint.resources.follows": "Follows",
-  "timeline_hint.resources.statuses": "Older toots",
+  "timeline_hint.resources.followers": "പിന്തുടരുന്നവർ",
+  "timeline_hint.resources.follows": "പിന്തുടരുന്നു",
+  "timeline_hint.resources.statuses": "പഴയ ടൂട്ടുകൾ",
   "trends.counter_by_accounts": "{count, plural, one {{counter} person} other {{counter} people}} talking",
-  "trends.trending_now": "Trending now",
+  "trends.trending_now": "ഇപ്പോൾ ട്രെൻഡിംഗ്",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "units.short.billion": "{count}B",
-  "units.short.million": "{count}M",
+  "units.short.million": "{count}ദശലക്ഷം",
   "units.short.thousand": "{count}K",
-  "upload_area.title": "Drag & drop to upload",
-  "upload_button.label": "Add images, a video or an audio file",
-  "upload_error.limit": "File upload limit exceeded.",
+  "upload_area.title": "അപ്‌ലോഡുചെയ്യാൻ വലിച്ചിടുക",
+  "upload_button.label": "ഇമേജുകൾ, ഒരു വീഡിയോ അല്ലെങ്കിൽ ഓഡിയോ ഫയൽ ചേർക്കുക",
+  "upload_error.limit": "ഫയൽ അപ്‌ലോഡ് പരിധി കവിഞ്ഞു.",
   "upload_error.poll": "File upload not allowed with polls.",
-  "upload_form.audio_description": "Describe for people with hearing loss",
-  "upload_form.description": "Describe for the visually impaired",
-  "upload_form.edit": "Edit",
-  "upload_form.thumbnail": "Change thumbnail",
-  "upload_form.undo": "Delete",
+  "upload_form.audio_description": "കേൾവിശക്തി ഇല്ലാത്തവർക്ക് വേണ്ടി വിവരണം നൽകൂ",
+  "upload_form.description": "കാഴ്ചശക്തി ഇല്ലാത്തവർക്ക് വേണ്ടി വിവരണം നൽകൂ",
+  "upload_form.edit": "തിരുത്തുക",
+  "upload_form.thumbnail": "ലഘുചിത്രം മാറ്റുക",
+  "upload_form.undo": "ഇല്ലാതാക്കുക",
   "upload_form.video_description": "Describe for people with hearing loss or visual impairment",
   "upload_modal.analyzing_picture": "Analyzing picture…",
-  "upload_modal.apply": "Apply",
-  "upload_modal.choose_image": "Choose image",
+  "upload_modal.apply": "പ്രയോഗിക്കുക",
+  "upload_modal.choose_image": "ചിത്രം തിരഞ്ഞെടുക്കുക",
   "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog",
   "upload_modal.detect_text": "Detect text from picture",
-  "upload_modal.edit_media": "Edit media",
+  "upload_modal.edit_media": "മീഡിയ തിരുത്തുക",
   "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.",
   "upload_modal.preparing_ocr": "Preparing OCR…",
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "Uploading…",
-  "video.close": "Close video",
-  "video.download": "Download file",
+  "video.close": "വീഡിയോ അടയ്ക്കുക",
+  "video.download": "ഫയൽ ഡൌൺലോഡ് ചെയ്യുക",
   "video.exit_fullscreen": "Exit full screen",
   "video.expand": "Expand video",
-  "video.fullscreen": "Full screen",
-  "video.hide": "Hide video",
+  "video.fullscreen": "പൂർണ്ണ സ്ക്രീൻ",
+  "video.hide": "വീഡിയോ മറയ്ക്കുക",
   "video.mute": "Mute sound",
   "video.pause": "Pause",
   "video.play": "Play",
diff --git a/app/javascript/mastodon/locales/mr.json b/app/javascript/mastodon/locales/mr.json
index 33f8cfd84..8559665b9 100644
--- a/app/javascript/mastodon/locales/mr.json
+++ b/app/javascript/mastodon/locales/mr.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Search results",
   "emoji_button.symbols": "Symbols",
   "emoji_button.travel": "Travel & Places",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Next",
   "lightbox.previous": "Previous",
-  "lightbox.view_context": "View context",
   "lists.account.add": "Add to list",
   "lists.account.remove": "Remove from list",
   "lists.delete": "Delete list",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Change title",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mentions",
   "notifications.filter.polls": "Poll results",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} notifications",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/ms.json b/app/javascript/mastodon/locales/ms.json
index a4006a745..ec992c679 100644
--- a/app/javascript/mastodon/locales/ms.json
+++ b/app/javascript/mastodon/locales/ms.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Search results",
   "emoji_button.symbols": "Symbols",
   "emoji_button.travel": "Travel & Places",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Next",
   "lightbox.previous": "Previous",
-  "lightbox.view_context": "View context",
   "lists.account.add": "Add to list",
   "lists.account.remove": "Remove from list",
   "lists.delete": "Delete list",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Change title",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mentions",
   "notifications.filter.polls": "Poll results",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} notifications",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index acab31394..095114255 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -9,10 +9,10 @@
   "account.browse_more_on_origin_server": "Meer op het originele profiel bekijken",
   "account.cancel_follow_request": "Volgverzoek annuleren",
   "account.direct": "@{name} een direct bericht sturen",
-  "account.disable_notifications": "Stop notifying me when @{name} posts",
+  "account.disable_notifications": "Geef geen melding meer wanneer @{name} toot",
   "account.domain_blocked": "Server verborgen",
   "account.edit_profile": "Profiel bewerken",
-  "account.enable_notifications": "Notify me when @{name} posts",
+  "account.enable_notifications": "Geef een melding wanneer @{name} toot",
   "account.endorse": "Op profiel weergeven",
   "account.follow": "Volgen",
   "account.followers": "Volgers",
@@ -98,9 +98,9 @@
   "compose_form.poll.switch_to_single": "Poll wijzigen om een enkele keuze toe te staan",
   "compose_form.publish": "Toot",
   "compose_form.publish_loud": "{publish}!",
-  "compose_form.sensitive.hide": "Media als gevoelig markeren",
-  "compose_form.sensitive.marked": "Media is als gevoelig gemarkeerd",
-  "compose_form.sensitive.unmarked": "Media is niet als gevoelig gemarkeerd",
+  "compose_form.sensitive.hide": "{count, plural, one {Media als gevoelig markeren} other {Media als gevoelig markeren}}",
+  "compose_form.sensitive.marked": "{count, plural, one {Media is als gevoelig gemarkeerd} other {Media is als gevoelig gemarkeerd}}",
+  "compose_form.sensitive.unmarked": "{count, plural, one {Media is niet als gevoelig gemarkeerd} other {Media is niet als gevoelig gemarkeerd}}",
   "compose_form.spoiler.marked": "Tekst is achter een waarschuwing verborgen",
   "compose_form.spoiler.unmarked": "Tekst is niet verborgen",
   "compose_form.spoiler_placeholder": "Waarschuwingstekst",
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Zoekresultaten",
   "emoji_button.symbols": "Symbolen",
   "emoji_button.travel": "Reizen en plekken",
+  "empty_column.account_suspended": "Account opgeschort",
   "empty_column.account_timeline": "Hier zijn geen toots!",
   "empty_column.account_unavailable": "Profiel is niet beschikbaar",
   "empty_column.blocks": "Jij hebt nog geen enkele gebruiker geblokkeerd.",
@@ -168,9 +169,9 @@
   "empty_column.notifications": "Je hebt nog geen meldingen. Begin met iemand een gesprek.",
   "empty_column.public": "Er is hier helemaal niks! Toot iets in het openbaar of volg mensen van andere servers om het te vullen",
   "error.unexpected_crash.explanation": "Als gevolg van een bug in onze broncode of als gevolg van een compatibiliteitsprobleem met jouw webbrowser, kan deze pagina niet goed worden weergegeven.",
-  "error.unexpected_crash.explanation_addons": "This page could not be displayed correctly. This error is likely caused by a browser add-on or automatic translation tools.",
+  "error.unexpected_crash.explanation_addons": "Deze pagina kon niet correct geladen worden. Deze fout wordt waarschijnlijk door een browser-add-on of een automatische vertalingshulpmiddel veroorzaakt.",
   "error.unexpected_crash.next_steps": "Probeer deze pagina te vernieuwen. Wanneer dit niet helpt is het nog steeds mogelijk om Mastodon in een andere webbrowser of mobiele app te gebruiken.",
-  "error.unexpected_crash.next_steps_addons": "Try disabling them and refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.",
+  "error.unexpected_crash.next_steps_addons": "Probeer deze uit te schakelen en de pagina te verversen. Wanneer dat niet helpt, kun je Mastodon nog altijd met een andere webbrowser of mobiele app gebruiken.",
   "errors.unexpected_crash.copy_stacktrace": "Stacktrace naar klembord kopiëren",
   "errors.unexpected_crash.report_issue": "Technisch probleem melden",
   "follow_request.authorize": "Goedkeuren",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Afbeelding groot weergeven",
   "lightbox.next": "Volgende",
   "lightbox.previous": "Vorige",
-  "lightbox.view_context": "Context tonen",
   "lists.account.add": "Aan lijst toevoegen",
   "lists.account.remove": "Uit lijst verwijderen",
   "lists.delete": "Lijst verwijderen",
@@ -266,10 +266,10 @@
   "lists.edit.submit": "Titel veranderen",
   "lists.new.create": "Lijst toevoegen",
   "lists.new.title_placeholder": "Naam nieuwe lijst",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
-  "lists.replies_policy.title": "Show replies to:",
+  "lists.replies_policy.followed": "Elke gevolgde gebruiker",
+  "lists.replies_policy.list": "Leden van de lijst",
+  "lists.replies_policy.none": "Niemand",
+  "lists.replies_policy.title": "Toon reacties aan:",
   "lists.search": "Zoek naar mensen die je volgt",
   "lists.subheading": "Jouw lijsten",
   "load_pending": "{count, plural, one {# nieuw item} other {# nieuwe items}}",
@@ -277,7 +277,7 @@
   "media_gallery.toggle_visible": "Media verbergen",
   "missing_indicator.label": "Niet gevonden",
   "missing_indicator.sublabel": "Deze hulpbron kan niet gevonden worden",
-  "mute_modal.duration": "Duration",
+  "mute_modal.duration": "Duur",
   "mute_modal.hide_notifications": "Verberg meldingen van deze persoon?",
   "mute_modal.indefinite": "Voor onbepaalde tijd",
   "navigation_bar.apps": "Mobiele apps",
@@ -310,7 +310,7 @@
   "notification.own_poll": "Jouw poll is beëindigd",
   "notification.poll": "Een poll waaraan jij hebt meegedaan is beëindigd",
   "notification.reblog": "{name} boostte jouw toot",
-  "notification.status": "{name} just posted",
+  "notification.status": "{name} heeft zojuist een toot geplaatst",
   "notifications.clear": "Meldingen verwijderen",
   "notifications.clear_confirmation": "Weet je het zeker dat je al jouw meldingen wilt verwijderen?",
   "notifications.column_settings.alert": "Desktopmeldingen",
@@ -326,22 +326,24 @@
   "notifications.column_settings.reblog": "Boosts:",
   "notifications.column_settings.show": "In kolom tonen",
   "notifications.column_settings.sound": "Geluid afspelen",
-  "notifications.column_settings.status": "New toots:",
+  "notifications.column_settings.status": "Nieuwe toots:",
   "notifications.filter.all": "Alles",
   "notifications.filter.boosts": "Boosts",
   "notifications.filter.favourites": "Favorieten",
   "notifications.filter.follows": "Die jij volgt",
   "notifications.filter.mentions": "Vermeldingen",
   "notifications.filter.polls": "Pollresultaten",
-  "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.filter.statuses": "Updates van mensen die je volgt",
+  "notifications.grant_permission": "Toestemming geven.",
   "notifications.group": "{count} meldingen",
-  "notifications.mark_as_read": "Mark every notification as read",
-  "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
-  "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
-  "notifications_permission_banner.enable": "Notificatie meldingen inschakelen",
-  "notifications_permission_banner.how_to_control": "Gebruikt notificaties om ook meldingen te ontvangen wanneer Mastodon niet open is. U kunt precies bepalen welke soort meldingen wel of geen notificaties afgeven via de bovenstaande knop {icon}.",
-  "notifications_permission_banner.title": "Never miss a thing",
-  "picture_in_picture.restore": "Put it back",
+  "notifications.mark_as_read": "Markeer elke melding als gelezen",
+  "notifications.permission_denied": "Desktopmeldingen zijn niet beschikbaar omdat een eerdere browsertoestemming werd geweigerd",
+  "notifications.permission_denied_alert": "Desktopmeldingen kunnen niet worden ingeschakeld, omdat een eerdere browsertoestemming werd geweigerd",
+  "notifications.permission_required": "Desktopmeldingen zijn niet beschikbaar omdat de benodigde toestemming niet is verleend.",
+  "notifications_permission_banner.enable": "Desktopmeldingen inschakelen",
+  "notifications_permission_banner.how_to_control": "Om meldingen te ontvangen wanneer Mastodon niet open is. Je kunt precies bepalen welke soort interacties wel of geen desktopmeldingen geven via de bovenstaande {icon} knop.",
+  "notifications_permission_banner.title": "Mis nooit meer iets",
+  "picture_in_picture.restore": "Terugzetten",
   "poll.closed": "Gesloten",
   "poll.refresh": "Vernieuwen",
   "poll.total_people": "{count, plural, one {# persoon} other {# mensen}}",
@@ -448,9 +450,9 @@
   "trends.counter_by_accounts": "{count, plural, one {{counter} persoon} other {{counter} personen}} zijn aan het praten",
   "trends.trending_now": "Trends",
   "ui.beforeunload": "Je concept zal verloren gaan als je Mastodon verlaat.",
-  "units.short.billion": "{count} miljard",
-  "units.short.million": "{count} miljoen",
-  "units.short.thousand": "{count} duizend",
+  "units.short.billion": "{count} mrd.",
+  "units.short.million": "{count} mln.",
+  "units.short.thousand": "{count}k",
   "upload_area.title": "Hiernaar toe slepen om te uploaden",
   "upload_button.label": "Afbeeldingen, een video- of een geluidsbestand toevoegen",
   "upload_error.limit": "Uploadlimiet van bestand overschreden.",
diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json
index f6518afab..38a95533c 100644
--- a/app/javascript/mastodon/locales/nn.json
+++ b/app/javascript/mastodon/locales/nn.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Søkeresultat",
   "emoji_button.symbols": "Symbol",
   "emoji_button.travel": "Reise & stader",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "Ingen tut her!",
   "empty_column.account_unavailable": "Profil ikkje tilgjengelig",
   "empty_column.blocks": "Du har ikkje blokkert nokon brukarar enno.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Neste",
   "lightbox.previous": "Førre",
-  "lightbox.view_context": "Sjå kontekst",
   "lists.account.add": "Legg til i liste",
   "lists.account.remove": "Fjern frå liste",
   "lists.delete": "Slett liste",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Endre tittel",
   "lists.new.create": "Legg til liste",
   "lists.new.title_placeholder": "Ny listetittel",
-  "lists.replies_policy.all_replies": "Enhver fulgt bruker",
-  "lists.replies_policy.list_replies": "Medlemmer i listen",
-  "lists.replies_policy.no_replies": "Ingen",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Vis svar på:",
   "lists.search": "Søk gjennom folk du følgjer",
   "lists.subheading": "Dine lister",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Nemningar",
   "notifications.filter.polls": "Røysteresultat",
   "notifications.filter.statuses": "Oppdateringer fra folk du følger",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} varsel",
   "notifications.mark_as_read": "Merk alle varsler som lest",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Skru på skrivebordsvarsler",
   "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.title": "Aldri gå glipp av noe",
diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json
index 46cf7c407..e64bf4a5a 100644
--- a/app/javascript/mastodon/locales/no.json
+++ b/app/javascript/mastodon/locales/no.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Søkeresultat",
   "emoji_button.symbols": "Symboler",
   "emoji_button.travel": "Reise & steder",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "Ingen tuter er her!",
   "empty_column.account_unavailable": "Profilen er utilgjengelig",
   "empty_column.blocks": "Du har ikke blokkert noen brukere enda.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Neste",
   "lightbox.previous": "Forrige",
-  "lightbox.view_context": "Vis sammenheng",
   "lists.account.add": "Legg til i listen",
   "lists.account.remove": "Fjern fra listen",
   "lists.delete": "Slett listen",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Endre tittel",
   "lists.new.create": "Ligg til liste",
   "lists.new.title_placeholder": "Ny listetittel",
-  "lists.replies_policy.all_replies": "Enhver fulgt bruker",
-  "lists.replies_policy.list_replies": "Medlemmer i listen",
-  "lists.replies_policy.no_replies": "Ingen",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Vis svar på:",
   "lists.search": "Søk blant personer du følger",
   "lists.subheading": "Dine lister",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Nevnelser",
   "notifications.filter.polls": "Avstemningsresultater",
   "notifications.filter.statuses": "Oppdateringer fra folk du følger",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} varslinger",
   "notifications.mark_as_read": "Merk alle varsler som lest",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Skru på skrivebordsvarsler",
   "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.title": "Aldri gå glipp av noe",
diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index f166f95e2..f56d337d3 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Resultats de recèrca",
   "emoji_button.symbols": "Simbòls",
   "emoji_button.travel": "Viatges & lòcs",
+  "empty_column.account_suspended": "Compte suspendut",
   "empty_column.account_timeline": "Cap de tuts aquí !",
   "empty_column.account_unavailable": "Perfil pas disponible",
   "empty_column.blocks": "Avètz pas blocat degun pel moment.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Espandir la fenèstra de visualizacion d’imatge",
   "lightbox.next": "Seguent",
   "lightbox.previous": "Precedent",
-  "lightbox.view_context": "Veire lo contèxt",
   "lists.account.add": "Ajustar a la lista",
   "lists.account.remove": "Levar de la lista",
   "lists.delete": "Suprimir la lista",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Cambiar lo títol",
   "lists.new.create": "Ajustar una lista",
   "lists.new.title_placeholder": "Títol de la nòva lista",
-  "lists.replies_policy.all_replies": "Los que sègui",
-  "lists.replies_policy.list_replies": "Membres d’aquesta lista",
-  "lists.replies_policy.no_replies": "Degun",
+  "lists.replies_policy.followed": "Quin seguidor que siá",
+  "lists.replies_policy.list": "Membres de la lista",
+  "lists.replies_policy.none": "Degun",
   "lists.replies_policy.title": "Mostrar las responsas a :",
   "lists.search": "Cercar demest lo mond que seguètz",
   "lists.subheading": "Vòstras listas",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mencions",
   "notifications.filter.polls": "Resultats del sondatge",
   "notifications.filter.statuses": "Mesas a jorn del monde que seguissètz",
+  "notifications.grant_permission": "Acordar l’autorizacion.",
   "notifications.group": "{count} notificacions",
   "notifications.mark_as_read": "Marcar totas las notificacions coma legidas",
   "notifications.permission_denied": "Las notificacion burèu son pas disponiblas a causa del refús de las demandas d’autorizacion navigador",
   "notifications.permission_denied_alert": "Las notificacions burèu son pas activada, per çò que las autorizacions son estadas refusada abans",
+  "notifications.permission_required": "Las notificacions de burèu son pas indisponiblas perque las permissions requeridas son pas estadas acordadas.",
   "notifications_permission_banner.enable": "Activar las notificacions burèu",
   "notifications_permission_banner.how_to_control": "Per recebre las notificacions de Mastodon quand es pas dobèrt, activatz las notificacions de burèu. Podètz precisar quin tipe de notificacion generarà una notificacion de burèu via lo boton {icon} dessús un còp activadas.",
   "notifications_permission_banner.title": "Manquetz pas jamai res",
diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json
index 813a6df29..f03a0086e 100644
--- a/app/javascript/mastodon/locales/pl.json
+++ b/app/javascript/mastodon/locales/pl.json
@@ -13,7 +13,7 @@
   "account.domain_blocked": "Ukryto domenę",
   "account.edit_profile": "Edytuj profil",
   "account.enable_notifications": "Powiadamiaj mnie o wpisach @{name}",
-  "account.endorse": "Polecaj na profilu",
+  "account.endorse": "Wyróżnij na profilu",
   "account.follow": "Śledź",
   "account.followers": "Śledzący",
   "account.followers.empty": "Nikt jeszcze nie śledzi tego użytkownika.",
@@ -153,6 +153,7 @@
   "emoji_button.search_results": "Wyniki wyszukiwania",
   "emoji_button.symbols": "Symbole",
   "emoji_button.travel": "Podróże i miejsca",
+  "empty_column.account_suspended": "Konto zawieszone",
   "empty_column.account_timeline": "Brak wpisów tutaj!",
   "empty_column.account_unavailable": "Profil niedostępny",
   "empty_column.blocks": "Nie zablokowałeś(-aś) jeszcze żadnego użytkownika.",
@@ -262,7 +263,6 @@
   "lightbox.expand": "Rozwiń pole widoku obrazu",
   "lightbox.next": "Następne",
   "lightbox.previous": "Poprzednie",
-  "lightbox.view_context": "Pokaż kontekst",
   "lists.account.add": "Dodaj do listy",
   "lists.account.remove": "Usunąć z listy",
   "lists.delete": "Usuń listę",
@@ -270,9 +270,9 @@
   "lists.edit.submit": "Zmień tytuł",
   "lists.new.create": "Utwórz listę",
   "lists.new.title_placeholder": "Wprowadź tytuł listy",
-  "lists.replies_policy.all_replies": "Dowolnego obserwowanego użytkownika",
-  "lists.replies_policy.list_replies": "Członków listy",
-  "lists.replies_policy.no_replies": "Nikogo",
+  "lists.replies_policy.followed": "Dowolny obserwowany użytkownik",
+  "lists.replies_policy.list": "Członkowie listy",
+  "lists.replies_policy.none": "Nikt",
   "lists.replies_policy.title": "Pokazuj odpowiedzi dla:",
   "lists.search": "Szukaj wśród osób które śledzisz",
   "lists.subheading": "Twoje listy",
@@ -339,10 +339,12 @@
   "notifications.filter.mentions": "Wspomienia",
   "notifications.filter.polls": "Wyniki głosowania",
   "notifications.filter.statuses": "Aktualizacje od osób które obserwujesz",
+  "notifications.grant_permission": "Przyznaj uprawnienia.",
   "notifications.group": "{count, number} {count, plural, one {powiadomienie} few {powiadomienia} many {powiadomień} more {powiadomień}}",
   "notifications.mark_as_read": "Oznacz wszystkie powiadomienia jako przeczytane",
   "notifications.permission_denied": "Powiadomienia na pulpicie nie są dostępne, ponieważ wcześniej nie udzielono uprawnień w przeglądarce",
   "notifications.permission_denied_alert": "Powiadomienia na pulpicie nie mogą zostać włączone, ponieważ wcześniej odmówiono uprawnień",
+  "notifications.permission_required": "Powiadomienia na pulpicie nie są dostępne, ponieważ nie przyznano wymaganego uprawnienia.",
   "notifications_permission_banner.enable": "Włącz powiadomienia na pulpicie",
   "notifications_permission_banner.how_to_control": "Aby otrzymywać powiadomienia, gdy Mastodon nie jest otwarty, włącz powiadomienia pulpitu. Możesz dokładnie kontrolować, októrych działaniach będziesz powiadomienia na pulpicie za pomocą przycisku {icon} powyżej, jeżeli tylko zostaną włączone.",
   "notifications_permission_banner.title": "Nie przegap niczego",
diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json
index abbaced58..2e0adfa9c 100644
--- a/app/javascript/mastodon/locales/pt-BR.json
+++ b/app/javascript/mastodon/locales/pt-BR.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Resultados da pesquisa",
   "emoji_button.symbols": "Símbolos",
   "emoji_button.travel": "Viagem & Lugares",
+  "empty_column.account_suspended": "Conta suspensa",
   "empty_column.account_timeline": "Nada aqui!",
   "empty_column.account_unavailable": "Perfil indisponível",
   "empty_column.blocks": "Nada aqui.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expandir caixa de visualização de imagem",
   "lightbox.next": "Próximo",
   "lightbox.previous": "Anterior",
-  "lightbox.view_context": "Ver contexto",
   "lists.account.add": "Adicionar à lista",
   "lists.account.remove": "Remover da lista",
   "lists.delete": "Excluir lista",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Renomear",
   "lists.new.create": "Criar lista",
   "lists.new.title_placeholder": "Nome da lista",
-  "lists.replies_policy.all_replies": "Qualquer usuário seguido",
-  "lists.replies_policy.list_replies": "Membros da lista",
-  "lists.replies_policy.no_replies": "Ninguém",
+  "lists.replies_policy.followed": "Qualquer usuário seguido",
+  "lists.replies_policy.list": "Membros da lista",
+  "lists.replies_policy.none": "Ninguém",
   "lists.replies_policy.title": "Mostrar respostas para:",
   "lists.search": "Procurar entre as pessoas que você segue",
   "lists.subheading": "Suas listas",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Menções",
   "notifications.filter.polls": "Resultados de enquete",
   "notifications.filter.statuses": "Atualizações de pessoas que você segue",
+  "notifications.grant_permission": "Conceder permissão.",
   "notifications.group": "{count} notificações",
   "notifications.mark_as_read": "Marcar todas as notificações como lidas",
   "notifications.permission_denied": "Não é possível habilitar as notificações da área de trabalho pois a permissão foi negada.",
   "notifications.permission_denied_alert": "As notificações da área de trabalho não podem ser habilitdas pois a permissão do navegador foi negada antes",
+  "notifications.permission_required": "Notificações da área de trabalho não estão disponíveis porque a permissão necessária não foi concedida.",
   "notifications_permission_banner.enable": "Habilitar notificações da área de trabalho",
   "notifications_permission_banner.how_to_control": "Para receber notificações quando o Mastodon não estiver aberto, habilite as notificações da área de trabalho. Você pode controlar precisamente quais tipos de interações geram notificações da área de trabalho através do botão {icon} acima uma vez habilitadas.",
   "notifications_permission_banner.title": "Nunca perca nada",
diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json
index 065a6bf57..dd3b4eba2 100644
--- a/app/javascript/mastodon/locales/pt-PT.json
+++ b/app/javascript/mastodon/locales/pt-PT.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Resultados da pesquisa",
   "emoji_button.symbols": "Símbolos",
   "emoji_button.travel": "Viagens & Lugares",
+  "empty_column.account_suspended": "Conta suspensa",
   "empty_column.account_timeline": "Sem toots por aqui!",
   "empty_column.account_unavailable": "Perfil indisponível",
   "empty_column.blocks": "Ainda não bloqueaste qualquer utilizador.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expandir caixa de visualização de imagem",
   "lightbox.next": "Próximo",
   "lightbox.previous": "Anterior",
-  "lightbox.view_context": "Ver contexto",
   "lists.account.add": "Adicionar à lista",
   "lists.account.remove": "Remover da lista",
   "lists.delete": "Remover lista",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Mudar o título",
   "lists.new.create": "Adicionar lista",
   "lists.new.title_placeholder": "Título da nova lista",
-  "lists.replies_policy.all_replies": "Qualquer utilizador seguido",
-  "lists.replies_policy.list_replies": "Membros da lista",
-  "lists.replies_policy.no_replies": "Ninguém",
+  "lists.replies_policy.followed": "Qualquer utilizador seguido",
+  "lists.replies_policy.list": "Membros da lista",
+  "lists.replies_policy.none": "Ninguém",
   "lists.replies_policy.title": "Mostrar respostas para:",
   "lists.search": "Pesquisa entre as pessoas que segues",
   "lists.subheading": "As tuas listas",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Menções",
   "notifications.filter.polls": "Votações",
   "notifications.filter.statuses": "Atualizações de pessoas que você segue",
+  "notifications.grant_permission": "Conceder permissões.",
   "notifications.group": "{count} notificações",
   "notifications.mark_as_read": "Marcar todas as notificações como lidas",
   "notifications.permission_denied": "Notificações no ambiente de trabalho não estão disponíveis porque a permissão, solicitada pelo navegador, foi recusada anteriormente",
   "notifications.permission_denied_alert": "Notificações no ambinente de trabalho não podem ser ativadas, pois a permissão do navegador foi recusada anteriormente",
+  "notifications.permission_required": "Notificações no ambiente de trabalho não estão disponíveis porque a permissão necessária não foi concedida.",
   "notifications_permission_banner.enable": "Ativar notificações no ambiente de trabalho",
   "notifications_permission_banner.how_to_control": "Para receber notificações quando o Mastodon não estiver aberto, ative as notificações no ambiente de trabalho. Depois da sua ativação, pode controlar precisamente quais tipos de interações geram notificações, através do botão {icon} acima.",
   "notifications_permission_banner.title": "Nunca perca nada",
diff --git a/app/javascript/mastodon/locales/ro.json b/app/javascript/mastodon/locales/ro.json
index dc1be7bc1..a80bfa822 100644
--- a/app/javascript/mastodon/locales/ro.json
+++ b/app/javascript/mastodon/locales/ro.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Rezultatele căutării",
   "emoji_button.symbols": "Simboluri",
   "emoji_button.travel": "Călătorii și Locuri",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "Nicio postare aici!",
   "empty_column.account_unavailable": "Profil indisponibil",
   "empty_column.blocks": "Nu ai blocat nici un utilizator încă.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Următorul",
   "lightbox.previous": "Precedentul",
-  "lightbox.view_context": "Vizualizați contextul",
   "lists.account.add": "Adaugă în listă",
   "lists.account.remove": "Elimină din listă",
   "lists.delete": "Șterge lista",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Schimbă titlul",
   "lists.new.create": "Adaugă listă",
   "lists.new.title_placeholder": "Titlu pentru noua listă",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Caută printre persoanele pe care le urmărești",
   "lists.subheading": "Listele tale",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Menționări",
   "notifications.filter.polls": "Rezultate sondaj",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} notificări",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json
index ae46adf82..3ce050759 100644
--- a/app/javascript/mastodon/locales/ru.json
+++ b/app/javascript/mastodon/locales/ru.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Результаты поиска",
   "emoji_button.symbols": "Символы",
   "emoji_button.travel": "Путешествия и места",
+  "empty_column.account_suspended": "Учетная запись заблокирована",
   "empty_column.account_timeline": "Здесь нет постов!",
   "empty_column.account_unavailable": "Профиль недоступен",
   "empty_column.blocks": "Вы ещё никого не заблокировали.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Развернуть окно просмотра изображений",
   "lightbox.next": "Далее",
   "lightbox.previous": "Назад",
-  "lightbox.view_context": "Контекст",
   "lists.account.add": "Добавить в список",
   "lists.account.remove": "Убрать из списка",
   "lists.delete": "Удалить список",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Изменить название",
   "lists.new.create": "Создать список",
   "lists.new.title_placeholder": "Название для нового списка",
-  "lists.replies_policy.all_replies": "Пользователям, на которых вы подписаны",
-  "lists.replies_policy.list_replies": "Пользователям в списке",
-  "lists.replies_policy.no_replies": "Никому",
+  "lists.replies_policy.followed": "Любой подписанный пользователь",
+  "lists.replies_policy.list": "Пользователи в списке",
+  "lists.replies_policy.none": "Никого",
   "lists.replies_policy.title": "Показать ответы только:",
   "lists.search": "Искать среди подписок",
   "lists.subheading": "Ваши списки",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Упоминания",
   "notifications.filter.polls": "Результаты опросов",
   "notifications.filter.statuses": "Обновления от людей, на которых вы подписаны",
+  "notifications.grant_permission": "Дать разрешение.",
   "notifications.group": "{count} уведомл.",
   "notifications.mark_as_read": "Отмечать все уведомления прочитанными",
   "notifications.permission_denied": "Уведомления на рабочем столе недоступны из-за ранее отклонённого запроса разрешений браузера",
   "notifications.permission_denied_alert": "Уведомления на рабочем столе не могут быть включены, так как раньше было отказано в разрешении браузера",
+  "notifications.permission_required": "Десктоп нотификации недоступны, потому что требуемое разрешение не было предоставлено.",
   "notifications_permission_banner.enable": "Включить уведомления на рабочем столе",
   "notifications_permission_banner.how_to_control": "Чтобы получать уведомления, когда Мастодон не открыт, включите уведомления рабочего стола. Вы можете точно управлять, какие типы взаимодействия генерируют уведомления рабочего стола с помощью кнопки {icon} выше, когда они включены.",
   "notifications_permission_banner.title": "Ничего не пропустите",
diff --git a/app/javascript/mastodon/locales/sa.json b/app/javascript/mastodon/locales/sa.json
index a8f5171c9..4656aa275 100644
--- a/app/javascript/mastodon/locales/sa.json
+++ b/app/javascript/mastodon/locales/sa.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "अन्वेषणपरिणामाः",
   "emoji_button.symbols": "चिह्नानि",
   "emoji_button.travel": "यात्रा च स्थानानि",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "न दौत्यान्यत्र",
   "empty_column.account_unavailable": "व्यक्तित्वं न प्राप्यते",
   "empty_column.blocks": "नैकोऽप्युपभोक्ता निषिद्धो वर्तते",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Next",
   "lightbox.previous": "Previous",
-  "lightbox.view_context": "View context",
   "lists.account.add": "Add to list",
   "lists.account.remove": "Remove from list",
   "lists.delete": "Delete list",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Change title",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mentions",
   "notifications.filter.polls": "Poll results",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} notifications",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/sc.json b/app/javascript/mastodon/locales/sc.json
index cf4ceb5d0..e1b4e3998 100644
--- a/app/javascript/mastodon/locales/sc.json
+++ b/app/javascript/mastodon/locales/sc.json
@@ -4,42 +4,42 @@
   "account.badges.bot": "Bot",
   "account.badges.group": "Grupu",
   "account.block": "Bloca @{name}",
-  "account.block_domain": "Bloca domìniu{domain}",
+  "account.block_domain": "Bloca su domìniu {domain}",
   "account.blocked": "Blocadu",
   "account.browse_more_on_origin_server": "Esplora de prus in su profilu originale",
   "account.cancel_follow_request": "Annulla rechesta de sighidura",
   "account.direct": "Messàgiu deretu a @{name}",
-  "account.disable_notifications": "Acaba·la de mi notificare cando @{name} publicat carchi cosa",
+  "account.disable_notifications": "Non mi notìfiches prus cando @{name} pùblichet messàgios",
   "account.domain_blocked": "Domìniu blocadu",
   "account.edit_profile": "Modìfica profilu",
-  "account.enable_notifications": "Notìfica·mi cando @{name} pùblicat carchi cosa",
+  "account.enable_notifications": "Notìfica·mi cando @{name} pùblicat messàgios",
   "account.endorse": "Cussìgia in su profilu tuo",
   "account.follow": "Sighi",
   "account.followers": "Sighiduras",
   "account.followers.empty": "Nemos sighit ancora custa persone.",
-  "account.followers_counter": "{count, plural, one {{counter} Sighidore} other {{counter} Sighidores}}",
-  "account.following_counter": "{count, plural, one {{counter} Sighidu} other {{counter} Sighidos}}",
+  "account.followers_counter": "{count, plural, one {{counter} sighidura} other {{counter} sighiduras}}",
+  "account.following_counter": "{count, plural, one {Sighende a {counter}} other {Sighende a {counter}}}",
   "account.follows.empty": "Custa persone non sighit ancora a nemos.",
   "account.follows_you": "Ti sighit",
   "account.hide_reblogs": "Cua is cumpartziduras de @{name}",
   "account.last_status": "Ùrtima atividade",
-  "account.link_verified_on": "Sa propiedade de custu ligàmene est istada controllada su {date}",
-  "account.locked_info": "Sa persone chi tenet sa propiedade revisionat a manu chie dda podet sighire.",
+  "account.link_verified_on": "Sa propiedade de custu ligòngiu est istada controllada su {date}",
+  "account.locked_info": "S'istadu de riservadesa de custu contu est istadu cunfiguradu comente blocadu. Sa persone chi tenet sa propiedade revisionat a manu chie dda podet sighire.",
   "account.media": "Cuntenutu multimediale",
-  "account.mention": "Mentova @{name}",
-  "account.moved_to": "{name} est istadu trasferidu a:",
-  "account.mute": "Pone @name a sa muda",
-  "account.mute_notifications": "Notìficas disativadas dae @{name}",
+  "account.mention": "Mentova a @{name}",
+  "account.moved_to": "{name} at cambiadu a:",
+  "account.mute": "Pone a @{name} a sa muda",
+  "account.mute_notifications": "Disativa is notìficas de @{name}",
   "account.muted": "A sa muda",
   "account.never_active": "Mai",
   "account.posts": "Tuts",
   "account.posts_with_replies": "Tuts e rispostas",
   "account.report": "Signala @{name}",
-  "account.requested": "Incarca pro annullare sa rechesta de sighidura",
+  "account.requested": "Abetende s'aprovatzione. Incarca pro annullare sa rechesta de sighidura",
   "account.share": "Cumpartzi su profilu de @{name}",
   "account.show_reblogs": "Ammustra is cumpartziduras de @{name}",
-  "account.statuses_counter": "{count, plural, one {{counter} Tut} other {{counter} Tuts}}",
-  "account.unblock": "Isbloca @{name}",
+  "account.statuses_counter": "{count, plural, one {{counter} tut} other {{counter} tuts}}",
+  "account.unblock": "Isbloca a @{name}",
   "account.unblock_domain": "Isbloca su domìniu {domain}",
   "account.unendorse": "Non cussiges in su profilu",
   "account.unfollow": "Non sigas prus",
@@ -49,7 +49,7 @@
   "alert.rate_limited.message": "Torra·bi a proare a pustis de {retry_time, time, medium}.",
   "alert.rate_limited.title": "Màssimu de rechestas barigadu",
   "alert.unexpected.message": "B'at àpidu una faddina.",
-  "alert.unexpected.title": "Oops!",
+  "alert.unexpected.title": "Oh!",
   "announcement.announcement": "Annùntziu",
   "autosuggest_hashtag.per_week": "{count} a sa chida",
   "boost_modal.combo": "Podes incarcare {combo} pro brincare custu sa borta chi benit",
@@ -73,7 +73,7 @@
   "column.notifications": "Notìficas",
   "column.pins": "Tuts apicados",
   "column.public": "Lìnia de tempus federada",
-  "column_back_button.label": "In segus",
+  "column_back_button.label": "A coa",
   "column_header.hide_settings": "Cua is cunfiguratziones",
   "column_header.moveLeft_settings": "Moe sa colunna a manca",
   "column_header.moveRight_settings": "Moe sa colunna a dereta",
@@ -81,13 +81,13 @@
   "column_header.show_settings": "Ammustra is cunfiguratziones",
   "column_header.unpin": "Boga dae pitzu",
   "column_subheading.settings": "Cunfiguratziones",
-  "community.column_settings.local_only": "Locale ebbia",
-  "community.column_settings.media_only": "Multimediale isceti",
-  "community.column_settings.remote_only": "Remotu ebbia",
+  "community.column_settings.local_only": "Isceti locale",
+  "community.column_settings.media_only": "Isceti multimediale",
+  "community.column_settings.remote_only": "Isceti remotu",
   "compose_form.direct_message_warning": "Custu tut at a èssere imbiadu isceti a is persones mentovadas.",
   "compose_form.direct_message_warning_learn_more": "Àteras informatziones",
-  "compose_form.hashtag_warning": "Custu tut no at a èssere ammustradu in peruna eticheta, dae chi no est listadu.",
-  "compose_form.lock_disclaimer": "Cale si siat persone ti podet sighire pro bìdere is messàgios tuos chi imbies a is chi ti sighint.",
+  "compose_form.hashtag_warning": "Custu tut no at a èssere ammustradu in peruna eticheta, dae chi no est listadu. Isceti is tuts pùblicos podent èssere chircados cun etichetas.",
+  "compose_form.lock_disclaimer": "Su contu tuo no est {locked}. Cale si siat persone ti podet sighire pro bìdere is messàgios tuos chi imbies a sa gente chi ti sighit.",
   "compose_form.lock_disclaimer.lock": "blocadu",
   "compose_form.placeholder": "A ite ses pensende?",
   "compose_form.poll.add_option": "Agiunghe unu sèberu",
@@ -98,10 +98,10 @@
   "compose_form.poll.switch_to_single": "Muda su sondàgiu pro permìtere un'optzione isceti",
   "compose_form.publish": "Tut",
   "compose_form.publish_loud": "{publish}!",
-  "compose_form.sensitive.hide": "Marca mèdia comente a sensìbile",
-  "compose_form.sensitive.marked": "Mèdia marcadu comente a sensìbile",
-  "compose_form.sensitive.unmarked": "Mèdia non marcadu comente a sensìbile",
-  "compose_form.spoiler.marked": "Su testu est cuadu dae s'avisu",
+  "compose_form.sensitive.hide": "{count, plural, one {Marca elementu multimediale comente a sensìbile} other {Marca elementos multimediales comente sensìbiles}}",
+  "compose_form.sensitive.marked": "{count, plural, one {Elementu multimediale marcadu comente a sensìbile} other {Elementos multimediales marcados comente a sensìbiles}}",
+  "compose_form.sensitive.unmarked": "{count, plural, one {Elementu multimediale non marcadu comente a sensìbile} other {Elementos multimediales non marcados comente a sensìbiles}}",
+  "compose_form.spoiler.marked": "Su testu est cuadu in fatu de s'avisu",
   "compose_form.spoiler.unmarked": "Su testu no est cuadu",
   "compose_form.spoiler_placeholder": "Iscrie s'avisu tuo inoghe",
   "confirmation_modal.cancel": "Annulla",
@@ -112,24 +112,24 @@
   "confirmations.delete.message": "Seguru chi boles cantzellare custu tut?",
   "confirmations.delete_list.confirm": "Cantzella",
   "confirmations.delete_list.message": "Seguru chi boles cantzellare custa lista in manera permanente?",
-  "confirmations.domain_block.confirm": "Cua totu su domìniu",
-  "confirmations.domain_block.message": "Ses seguru a beru, ma a beru a beru, de bòlere blocare su {domain} intreu? In sa parte manna de is casos pagos blocos o silentziamentos de utentes sunt sufitzientes e preferìbiles. No as a bìdere cuntenutos dae custu domìniu in peruna lìnia de tempus pùblica o in is notìficas tuas. Is sighidores tuos dae cussu domìniu ant a èssere bogados.",
+  "confirmations.domain_block.confirm": "Bloca totu su domìniu",
+  "confirmations.domain_block.message": "Boles de seguru, ma a beru a beru, blocare su {domain} intreu? In sa parte manna de is casos pagos blocos o silentziamentos de persones sunt sufitzientes e preferìbiles. No as a bìdere cuntenutos dae custu domìniu in peruna lìnia de tempus pùblica o in is notìficas tuas. Sa gente chi ti sighit dae cussu domìniu at a èssere bogada.",
   "confirmations.logout.confirm": "Essi·nche",
   "confirmations.logout.message": "Seguru chi boles essire?",
   "confirmations.mute.confirm": "A sa muda",
   "confirmations.mute.explanation": "Custu at a cuare is publicatziones issoro e is messàgios chi ddos mentovant, ma ant a pòdere bìdere is messàgios tuos e t'ant a pòdere sighire.",
-  "confirmations.mute.message": "Seguru chi boles pònnere {name} a sa muda?",
+  "confirmations.mute.message": "Seguru chi boles pònnere a {name} a sa muda?",
   "confirmations.redraft.confirm": "Cantzella e torra a fàghere",
-  "confirmations.redraft.message": "As a pèrdere is preferidos e is cumpartziduras, e is rispostas a su messàgiu originale ant a abarrare òrfanas.",
+  "confirmations.redraft.message": "Seguru chi boles cantzellare a torrare a fàghere custu tut? As a pèrdere is preferidos e is cumpartziduras, e is rispostas a su messàgiu originale ant a abarrare òrfanas.",
   "confirmations.reply.confirm": "Risponde",
   "confirmations.reply.message": "Rispondende immoe as a subraiscrìere su messàgiu chi ses iscriende. Seguru chi boles sighire?",
   "confirmations.unfollow.confirm": "Non sigas prus",
-  "confirmations.unfollow.message": "Seguru chi non boles sighire prus {name}?",
+  "confirmations.unfollow.message": "Seguru chi non boles sighire prus a {name}?",
   "conversation.delete": "Cantzella arresonada",
   "conversation.mark_as_read": "Signala comente lèghidu",
-  "conversation.open": "Bide arresonada",
+  "conversation.open": "Ammsutra arresonada",
   "conversation.with": "Cun {names}",
-  "directory.federated": "Dae unu fediversu connòschidu",
+  "directory.federated": "Dae unu fediversu connotu",
   "directory.local": "Isceti dae {domain}",
   "directory.new_arrivals": "Arribos noos",
   "directory.recently_active": "Ativos dae pagu",
@@ -149,14 +149,15 @@
   "emoji_button.search_results": "Resurtados de sa chirca",
   "emoji_button.symbols": "Sìmbulos",
   "emoji_button.travel": "Biàgios e logos",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "Perunu tut inoghe!",
   "empty_column.account_unavailable": "Su profilu no est a disponimentu",
-  "empty_column.blocks": "No as isblocadu ancora nemos.",
+  "empty_column.blocks": "No as blocadu ancora nemos.",
   "empty_column.bookmarked_statuses": "Non tenes ancora perunu tut in is marcadores. Cando nd'as a agiùnghere unu, at a èssere ammustradu inoghe.",
   "empty_column.community": "Sa lìnia de tempus locale est bòida. Iscrie inoghe pro cumintzare sa festa!",
   "empty_column.direct": "Non tenes ancora perunu messàgiu deretu. Cando nd'as a imbiare o nd'as a retzire unu, at a èssere ammustradu inoghe.",
   "empty_column.domain_blocks": "Non tenes ancora perunu domìniu blocadu.",
-  "empty_column.favourited_statuses": "Non tenes ancora perunu tut in is marcadores. Cando nd'as a agiùnghere unu, at a èssere ammustradu inoghe.",
+  "empty_column.favourited_statuses": "Non tenes ancora perunu tut in is preferidos. Cando nd'as a agiùnghere unu, at a èssere ammustradu inoghe.",
   "empty_column.favourites": "Nemos at marcadu ancora custu tut comente preferidu. Cando calicunu dd'at a fàghere, at a èssere ammustradu inoghe.",
   "empty_column.follow_requests": "Non tenes ancora peruna rechesta de sighidura. Cando nd'as a retzire una, at a èssere ammustrada inoghe.",
   "empty_column.hashtag": "Ancora nudda in custa eticheta.",
@@ -169,8 +170,8 @@
   "empty_column.public": "Nudda inoghe. Iscrie calicuna cosa pùblica, o sighi àteras persones de àteros serbidores pro prenare custu ispàtziu",
   "error.unexpected_crash.explanation": "A càusa de una faddina in su còdighe nostru o unu problema de cumpatibilidade de su navigadore, custa pàgina diat pòdere no èssere ammustrada in manera curreta.",
   "error.unexpected_crash.explanation_addons": "Custa pàgina diat pòdere no èssere ammustrada comente si tocat. Custa faddina est probàbile chi dipendat dae un'estensione de su navigadore o dae ainas automàticas de tradutzione.",
-  "error.unexpected_crash.next_steps": "Proa de atualizare sa pàgina. Si custu non acontza su problema, podes chircare de impreare Mastodon in unu navigadore diferente o in un'aplicatzione nativa.",
-  "error.unexpected_crash.next_steps_addons": "Proa a ddos disabilitare e a atualizare sa pàgina. Si custu non acontzat su problema, podes chircare de impreare Mastodon in unu navigadore diferente o in un'aplicatzione nativa.",
+  "error.unexpected_crash.next_steps": "Proa de torrare a carrigare sa pàgina. Si custu no acontza su problema, podes chircare de impreare Mastodon in unu navigadore diferente o in un'aplicatzione nativa.",
+  "error.unexpected_crash.next_steps_addons": "Proa a ddos disabilitare e torra a carrigare sa pàgina. Si custu no acontzat su problema, podes chircare de impreare Mastodon in unu navigadore diferente o in un'aplicatzione nativa.",
   "errors.unexpected_crash.copy_stacktrace": "Còpia stacktrace in punta de billete",
   "errors.unexpected_crash.report_issue": "Signala unu problema",
   "follow_request.authorize": "Autoriza",
@@ -199,14 +200,14 @@
   "home.column_settings.show_replies": "Ammustra rispostas",
   "home.hide_announcements": "Cua annùntzios",
   "home.show_announcements": "Ammustra annùntzios",
-  "intervals.full.days": "{number, plural, one {# die} other {# die}}",
+  "intervals.full.days": "{number, plural, one {# die} other {# dies}}",
   "intervals.full.hours": "{number, plural, one {# ora} other {# oras}}",
   "intervals.full.minutes": "{number, plural, one {# minutu} other {# minutos}}",
   "introduction.federation.action": "Sighi",
   "introduction.federation.federated.headline": "Federada",
   "introduction.federation.federated.text": "Is publicatziones pùblicas de àteros serbidores de su fediversu ant a aparèssere in sa lìnia de tempus federada.",
   "introduction.federation.home.headline": "Printzipale",
-  "introduction.federation.home.text": "Is messàgios de gente chi sighis ant a aparèssere in lìnia de tempus printzipale tua. Podes sighire gente de cale si siat serbidore.",
+  "introduction.federation.home.text": "Is messàgios de sa gente chi sighis ant a aparèssere in sa lìnia de tempus printzipale tua. Podes sighire gente de cale si siat serbidore!",
   "introduction.federation.local.headline": "Locale",
   "introduction.federation.local.text": "Is publicatziones pùblicas de sa gente de su pròpiu serbidore tuo ant a aparèssere in sa lìnia de tempus locale.",
   "introduction.interactions.action": "Acabba su tutoriale!",
@@ -218,16 +219,16 @@
   "introduction.interactions.reply.text": "Podes rispòndere a is tuts de àtera gente e a is tuos pròpios, e ant a èssere unidos in un'arresonada.",
   "introduction.welcome.action": "Ajò, andamus!",
   "introduction.welcome.headline": "Primos passos",
-  "introduction.welcome.text": "Ti donamus sa benebènnida a su fediversu. Dae immoe a pagu, as a pòdere publicare messàgios e chistionare cun is amistades tuas in meda serbidores. Però custu serbidore, {domain}, est ispetziale: allògiat su profilu tuo, duncas regorda·nde si nòmine.",
+  "introduction.welcome.text": "Ti donamus sa benebènnida a su fediversu. Dae immoe a pagu, as a pòdere publicare messàgios e chistionare cun is amistades tuas in meda serbidores. Però custu serbidore, {domain}, est ispetziale: allògiat su profilu tuo, duncas regorda·ti·nde su nòmine.",
   "keyboard_shortcuts.back": "pro navigare in segus",
   "keyboard_shortcuts.blocked": "pro abèrrere sa lista de persones blocadas",
   "keyboard_shortcuts.boost": "pro cumpartzire",
-  "keyboard_shortcuts.column": "pro atzentrare un'istadu in una de is colunnas",
+  "keyboard_shortcuts.column": "pro atzentrare unu tut in una de is colunnas",
   "keyboard_shortcuts.compose": "pro atzentrare in s'àrea de cumpositzione de testu",
   "keyboard_shortcuts.description": "Descritzione",
   "keyboard_shortcuts.direct": "pro abèrrere sa colunna de messàgios diretos",
   "keyboard_shortcuts.down": "pro mòere in bàsciu in sa lista",
-  "keyboard_shortcuts.enter": "pro abèrrere s'istadu",
+  "keyboard_shortcuts.enter": "pro abèrrere su tut",
   "keyboard_shortcuts.favourite": "pro marcare comente a preferidu",
   "keyboard_shortcuts.favourites": "pro abèrrere sa lista de preferidos",
   "keyboard_shortcuts.federated": "pro abèrrere sa lìnia de tempus federada",
@@ -240,7 +241,7 @@
   "keyboard_shortcuts.muted": "pro abèrrere sa lista de persones a sa muda",
   "keyboard_shortcuts.my_profile": "pro abèrrere su profilu tuo",
   "keyboard_shortcuts.notifications": "pro abèrrere sa colunna de notificatziones",
-  "keyboard_shortcuts.open_media": "pro abèrrere mèdia",
+  "keyboard_shortcuts.open_media": "pro abèrrere elementos multimediales",
   "keyboard_shortcuts.pinned": "pro abèrrere lista de tuts apicados",
   "keyboard_shortcuts.profile": "pro abèrrere su profilu de s'autore",
   "keyboard_shortcuts.reply": "pro rispòndere",
@@ -249,7 +250,7 @@
   "keyboard_shortcuts.spoilers": "pro ammustrare/cuare su campu AC",
   "keyboard_shortcuts.start": "pro abèrrere sa colunna \"Cumintza\"",
   "keyboard_shortcuts.toggle_hidden": "pro ammustrare o cuare testu de is AC",
-  "keyboard_shortcuts.toggle_sensitivity": "pro ammustrare o cuare mèdias",
+  "keyboard_shortcuts.toggle_sensitivity": "pro ammustrare o cuare elementos multimediales",
   "keyboard_shortcuts.toot": "pro cumintzare a iscrìere unu tut nou",
   "keyboard_shortcuts.unfocus": "pro essire de s'àrea de cumpositzione de testu o de chirca",
   "keyboard_shortcuts.up": "pro mòere in susu in sa lista",
@@ -258,26 +259,25 @@
   "lightbox.expand": "Ismànnia sa casella de visualizatzione de is immàgines",
   "lightbox.next": "Sighi",
   "lightbox.previous": "Pretzedente",
-  "lightbox.view_context": "Bide su cuntestu",
-  "lists.account.add": "Agiùnghe a sa lista",
+  "lists.account.add": "Agiunghe a sa lista",
   "lists.account.remove": "Boga dae sa lista",
   "lists.delete": "Cantzella sa lista",
   "lists.edit": "Modìfica sa lista",
   "lists.edit.submit": "Muda su tìtulu",
   "lists.new.create": "Agiunghe lista",
-  "lists.new.title_placeholder": "Lista noa",
-  "lists.replies_policy.all_replies": "Cale si siat utente sighidu",
-  "lists.replies_policy.list_replies": "Membros de sa lista",
-  "lists.replies_policy.no_replies": "Perunu",
+  "lists.new.title_placeholder": "Tìtulu de sa lista noa",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Ammustra is rispostas a:",
-  "lists.search": "Chircare intre sa gente chi ses sighende",
+  "lists.search": "Chirca intre sa gente chi ses sighende",
   "lists.subheading": "Is listas tuas",
   "load_pending": "{count, plural, one {# elementu nou} other {# elementos noos}}",
   "loading_indicator.label": "Carrighende...",
-  "media_gallery.toggle_visible": "Cua mèdia",
+  "media_gallery.toggle_visible": "Cua {number, plural, one {immàgine} other {immàgines}}",
   "missing_indicator.label": "Perunu resurtadu",
   "missing_indicator.sublabel": "Resursa no agatada",
-  "mute_modal.duration": "Duration",
+  "mute_modal.duration": "Durada",
   "mute_modal.hide_notifications": "Boles cuare is notìficas de custa persone?",
   "mute_modal.indefinite": "Indefinida",
   "navigation_bar.apps": "Aplicatziones mòbiles",
@@ -290,7 +290,7 @@
   "navigation_bar.domain_blocks": "Domìnios blocados",
   "navigation_bar.edit_profile": "Modìfica profilu",
   "navigation_bar.favourites": "Preferidos",
-  "navigation_bar.filters": "Paràulas a sa muda",
+  "navigation_bar.filters": "Faeddos a sa muda",
   "navigation_bar.follow_requests": "Rechestas de sighidura",
   "navigation_bar.follows_and_followers": "Persones chi sighis e chi ti sighint",
   "navigation_bar.info": "Informatziones de su serbidore",
@@ -309,7 +309,7 @@
   "notification.mention": "{name} t'at mentovadu",
   "notification.own_poll": "Sondàgiu acabbadu",
   "notification.poll": "Unu sondàgiu in su chi as votadu est acabbadu",
-  "notification.reblog": "{name} at cumpartzidu s'istadu tuo",
+  "notification.reblog": "{name} at cumpartzidu su tut tuo",
   "notification.status": "{name} at publicadu cosa",
   "notifications.clear": "Lìmpia notìficas",
   "notifications.clear_confirmation": "Seguru chi boles isboidare in manera permanente totu is notìficas tuas?",
@@ -318,8 +318,8 @@
   "notifications.column_settings.filter_bar.advanced": "Ammustra totu is categorias",
   "notifications.column_settings.filter_bar.category": "Barra lestra de filtros",
   "notifications.column_settings.filter_bar.show": "Ammustra",
-  "notifications.column_settings.follow": "Gente noa chi ti sighit:",
-  "notifications.column_settings.follow_request": "Rechesta de sighidura noa:",
+  "notifications.column_settings.follow": "Sighiduras noas:",
+  "notifications.column_settings.follow_request": "Rechestas noas de sighidura:",
   "notifications.column_settings.mention": "Mentovos:",
   "notifications.column_settings.poll": "Resurtados de su sondàgiu:",
   "notifications.column_settings.push": "Notìficas push",
@@ -332,20 +332,22 @@
   "notifications.filter.favourites": "Preferidos",
   "notifications.filter.follows": "Sighende",
   "notifications.filter.mentions": "Mentovos",
-  "notifications.filter.polls": "Resurtados dae su sondàgiu",
-  "notifications.filter.statuses": "Agiornamentos dae is persones chi sighis",
+  "notifications.filter.polls": "Resurtados de su sondàgiu",
+  "notifications.filter.statuses": "Atualizatziones dae gente chi sighis",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} notìficas",
-  "notifications.mark_as_read": "Mark every notification as read",
+  "notifications.mark_as_read": "Sinnala ònnia notìfica comente lèghida",
   "notifications.permission_denied": "Is notìficas de iscrivania non sunt a disponimentu pro neghe de rechestas de permissu chi sunt istadas dennegadas in antis",
   "notifications.permission_denied_alert": "Is notìficas de iscrivania non podent èssere abilitadas, ca su permissu de su navigadore est istadu dennegadu in antis",
-  "notifications_permission_banner.enable": "Abìlita is notìficas de iscrivania",
-  "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.title": "Never miss a thing",
-  "picture_in_picture.restore": "Put it back",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
+  "notifications_permission_banner.enable": "Abilita is notìficas de iscrivania",
+  "notifications_permission_banner.how_to_control": "Pro retzire notìficas cando Mastodon no est abertu, abilita is notìficas de iscrivania. Podes controllare cun pretzisione is castas de interatziones chi ingendrant notìficas de iscrivania pro mèdiu de su butone {icon} in subra, cando sunt abilitadas.",
+  "notifications_permission_banner.title": "Non ti perdas mai nudda",
+  "picture_in_picture.restore": "Torra·ddu a ue fiat",
   "poll.closed": "Serradu",
   "poll.refresh": "Atualiza",
-  "poll.total_people": "{count, plurale, one {# persone} other {# persones}}",
-  "poll.total_votes": "{count, plurale, one {# votu} other {# votos}}",
+  "poll.total_people": "{count, plural, one {# persone} other {# persones}}",
+  "poll.total_votes": "{count, plural, one {# votu} other {# votos}}",
   "poll.vote": "Vota",
   "poll.voted": "As votadu custa risposta",
   "poll_button.add_poll": "Agiunghe unu sondàgiu",
@@ -370,7 +372,7 @@
   "relative_time.today": "oe",
   "reply_indicator.cancel": "Annulla",
   "report.forward": "Torra a imbiare a {target}",
-  "report.forward_hint": "Custu contu est de un'àteru serbidore. Bi boles imbiare puru una còpia anònima de custu informe?",
+  "report.forward_hint": "Custu contu est de un'àteru serbidore. Ddi boles imbiare puru una còpia anònima de custu informe?",
   "report.hint": "S'informe at a èssere imbiadu a sa moderatzione de su serbidore. Podes frunire un'ispiegatzione de sa signalatzione tua de custu contu:",
   "report.placeholder": "Cummentos additzionales",
   "report.submit": "Imbia",
@@ -379,21 +381,21 @@
   "search_popout.search_format": "Formadu de chirca avantzada",
   "search_popout.tips.full_text": "Testu sèmplitze pro agatare istados chi as iscritu, marcadu comente a preferidos, cumpartzidu o chi t'ant mentovadu, e fintzas nòmines de utente, nòmines visualizados e etichetas chi ddu includent.",
   "search_popout.tips.hashtag": "eticheta",
-  "search_popout.tips.status": "istadu",
+  "search_popout.tips.status": "tut",
   "search_popout.tips.text": "Testu sèmplitze pro agatare nòmines visualizados, nòmines de utente e etichetas",
   "search_popout.tips.user": "utente",
   "search_results.accounts": "Gente",
   "search_results.hashtags": "Etichetas",
   "search_results.statuses": "Tuts",
   "search_results.statuses_fts_disabled": "Sa chirca de tuts pro su cuntenutu issoro no est abilitada in custu serbidore de Mastodon.",
-  "search_results.total": "{count, number} {count, plurale, one {resurtadu} other {resurtados}}",
+  "search_results.total": "{count, number} {count, plural, one {resurtadu} other {resurtados}}",
   "status.admin_account": "Aberi s'interfache de moderatzione pro @{name}",
   "status.admin_status": "Aberi custu istadu in s'interfache de moderatzione",
   "status.block": "Bloca @{name}",
-  "status.bookmark": "Marcadore",
+  "status.bookmark": "Sinnalibru",
   "status.cancel_reblog_private": "Iscontza sa cumpartzidura",
   "status.cannot_reblog": "Custa publicatzione non podet èssere cumpartzida",
-  "status.copy": "Còpia su ligàmene a s'istadu tuo",
+  "status.copy": "Còpia su ligòngiu a su tut tuo",
   "status.delete": "Cantzella",
   "status.detailed_status": "Visualizatzione de detàlliu de arresonada",
   "status.direct": "Messàgiu deretu a @{name}",
@@ -401,24 +403,24 @@
   "status.favourite": "Preferidos",
   "status.filtered": "Filtradu",
   "status.load_more": "Càrriga·nde àteros",
-  "status.media_hidden": "Mèdias cuados",
+  "status.media_hidden": "Elementos multimediales cuados",
   "status.mention": "Mentova @{name}",
   "status.more": "Àteru",
-  "status.mute": "Pone @name a sa muda",
+  "status.mute": "Pone @{name} a sa muda",
   "status.mute_conversation": "Pone s'arresonada a sa muda",
-  "status.open": "Ismànnia custu istadu",
+  "status.open": "Ismànnia custu tut",
   "status.pin": "Apica in su profilu",
   "status.pinned": "Tut apicadu",
-  "status.read_more": "Lèghe·nde àteru",
+  "status.read_more": "Leghe·nde àteru",
   "status.reblog": "Cumpartzi",
   "status.reblog_private": "Cumpartzi cun is utentes originales",
   "status.reblogged_by": "{name} at cumpartzidu",
   "status.reblogs.empty": "Nemos at ancora cumpartzidu custu tut. Cando calicunu dd'at a fàghere, at a èssere ammustradu inoghe.",
   "status.redraft": "Cantzella e torra a iscrìere",
-  "status.remove_bookmark": "Boga su marcadore",
+  "status.remove_bookmark": "Boga su sinnalibru",
   "status.reply": "Risponde",
   "status.replyAll": "Risponde a su tema",
-  "status.report": "Signala @{name}",
+  "status.report": "Sinnala @{name}",
   "status.sensitive_warning": "Cuntenutu sensìbile",
   "status.share": "Cumpartzi",
   "status.show_less": "Ammustra·nde prus pagu",
@@ -428,7 +430,7 @@
   "status.show_thread": "Ammustra su tema",
   "status.uncached_media_warning": "No est a disponimentu",
   "status.unmute_conversation": "Torra a ativare s'arresonada",
-  "status.unpin": "Isbloca dae pitzu de su profilu",
+  "status.unpin": "Boga dae pitzu de su profilu",
   "suggestions.dismiss": "Iscarta cussìgiu",
   "suggestions.header": "Est possìbile chi tèngias interessu in…",
   "tabs_bar.federated_timeline": "Federada",
@@ -441,34 +443,34 @@
   "time_remaining.minutes": "{number, plural, one {abarrat # minutu} other {abarrant # minutos}}",
   "time_remaining.moments": "Abarrant pagu momentos",
   "time_remaining.seconds": "{number, plural, one {abarrat # segundu} other {abarrant # segundos}}",
-  "timeline_hint.remote_resource_not_displayed": "{resource} from other servers are not displayed.",
-  "timeline_hint.resources.followers": "Followers",
-  "timeline_hint.resources.follows": "Follows",
-  "timeline_hint.resources.statuses": "Older toots",
-  "trends.counter_by_accounts": "{count, plural, one {{counter} person} other {{counter} people}} talking",
+  "timeline_hint.remote_resource_not_displayed": "{resource} dae àteros serbidores non benint ammustrados.",
+  "timeline_hint.resources.followers": "Sighiduras",
+  "timeline_hint.resources.follows": "Sighende",
+  "timeline_hint.resources.statuses": "Tuts prus betzos",
+  "trends.counter_by_accounts": "{count, plural, one {{counter} persone} other {{counter} persones}} chistionende",
   "trends.trending_now": "Est tendèntzia immoe",
   "ui.beforeunload": "S'abbotzu tuo at a èssere pèrdidu si essis dae Mastodon.",
-  "units.short.billion": "{count}B",
-  "units.short.million": "{count}M",
-  "units.short.thousand": "{count}K",
+  "units.short.billion": "{count}Mrd",
+  "units.short.million": "{count}Mln",
+  "units.short.thousand": "{count}m",
   "upload_area.title": "Traga pro carrigare",
-  "upload_button.label": "Agiunghe mèdias",
+  "upload_button.label": "Agiunghe immàgines, unu vìdeu o unu documentu sonoru",
   "upload_error.limit": "Lìmite de càrriga de archìvios barigadu.",
   "upload_error.poll": "Non si permitit s'imbiu de archìvios in is sondàgios.",
   "upload_form.audio_description": "Descritzione pro persones cun pèrdida auditiva",
   "upload_form.description": "Descritzione pro persones cun problemas visuales",
   "upload_form.edit": "Modìfica",
-  "upload_form.thumbnail": "Change thumbnail",
+  "upload_form.thumbnail": "Càmbia sa miniadura",
   "upload_form.undo": "Cantzella",
   "upload_form.video_description": "Descritzione pro persones cun pèrdida auditiva o problemas visuales",
   "upload_modal.analyzing_picture": "Analizende immàgine…",
   "upload_modal.apply": "Àplica",
-  "upload_modal.choose_image": "Choose image",
+  "upload_modal.choose_image": "Sèbera un'immàgine",
   "upload_modal.description_placeholder": "Su margiane castàngiu brincat lestru a subra de su cane mandrone",
   "upload_modal.detect_text": "Rileva testu de s'immàgine",
-  "upload_modal.edit_media": "Modìfica su mèdia",
-  "upload_modal.hint": "Incarca o traga su tzìrculu in sa previsualizatzione pro seberare su puntu focale chi at a èssere semper visìbile in totu is miniaturas.",
-  "upload_modal.preparing_ocr": "Preparing OCR…",
+  "upload_modal.edit_media": "Modìfica elementu multimediale",
+  "upload_modal.hint": "Incarca o traga su tzìrculu in sa previsualizatzione pro seberare su puntu focale chi at a èssere semper visìbile in totu is miniaduras.",
+  "upload_modal.preparing_ocr": "Ammaniende s'OCR…",
   "upload_modal.preview_label": "Previsualiza ({ratio})",
   "upload_progress.label": "Carrighende...",
   "video.close": "Serra su vìdeu",
diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json
index de64a9e52..9bb0aa8ce 100644
--- a/app/javascript/mastodon/locales/sk.json
+++ b/app/javascript/mastodon/locales/sk.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Nájdené",
   "emoji_button.symbols": "Symboly",
   "emoji_button.travel": "Cestovanie a miesta",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "Niesú tu žiadne príspevky!",
   "empty_column.account_unavailable": "Profil nedostupný",
   "empty_column.blocks": "Ešte si nikoho nezablokoval/a.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Ďalšie",
   "lightbox.previous": "Predchádzajúci",
-  "lightbox.view_context": "Ukáž kontext",
   "lists.account.add": "Pridaj do zoznamu",
   "lists.account.remove": "Odober zo zoznamu",
   "lists.delete": "Vymaž list",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Zmeň názov",
   "lists.new.create": "Pridaj zoznam",
   "lists.new.title_placeholder": "Názov nového zoznamu",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Vyhľadávaj medzi užívateľmi, ktorých sleduješ",
   "lists.subheading": "Tvoje zoznamy",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Iba spomenutia",
   "notifications.filter.polls": "Výsledky ankiet",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} oboznámení",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json
index dd25cbc03..ca29c4455 100644
--- a/app/javascript/mastodon/locales/sl.json
+++ b/app/javascript/mastodon/locales/sl.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Rezultati iskanja",
   "emoji_button.symbols": "Simboli",
   "emoji_button.travel": "Potovanja in Kraji",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "Tukaj ni tutov!",
   "empty_column.account_unavailable": "Profil ni na voljo",
   "empty_column.blocks": "Niste še blokirali nobenega uporabnika.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Naslednji",
   "lightbox.previous": "Prejšnji",
-  "lightbox.view_context": "Poglej kontekst",
   "lists.account.add": "Dodaj na seznam",
   "lists.account.remove": "Odstrani s seznama",
   "lists.delete": "Izbriši seznam",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Spremeni naslov",
   "lists.new.create": "Dodaj seznam",
   "lists.new.title_placeholder": "Nov naslov seznama",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Išči med ljudmi, katerim sledite",
   "lists.subheading": "Vaši seznami",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Omembe",
   "notifications.filter.polls": "Rezultati glasovanj",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} obvestil",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json
index 63feac7ff..b45f62cd5 100644
--- a/app/javascript/mastodon/locales/sq.json
+++ b/app/javascript/mastodon/locales/sq.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Përfundime kërkimi",
   "emoji_button.symbols": "Simbole",
   "emoji_button.travel": "Udhëtime & Vende",
+  "empty_column.account_suspended": "Llogaria u pezullua",
   "empty_column.account_timeline": "S’ka mesazhe këtu!",
   "empty_column.account_unavailable": "Profil jashtë funksionimi",
   "empty_column.blocks": "S’keni bllokuar ende ndonjë përdorues.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Zgjeroje kuadratin e parjes së figurave",
   "lightbox.next": "Pasuesja",
   "lightbox.previous": "E mëparshmja",
-  "lightbox.view_context": "Shihni kontekstin",
   "lists.account.add": "Shto në listë",
   "lists.account.remove": "Hiqe nga lista",
   "lists.delete": "Fshije listën",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Ndryshoni titullin",
   "lists.new.create": "Shtoni listë",
   "lists.new.title_placeholder": "Titull liste të re",
-  "lists.replies_policy.all_replies": "Cilido përdorues i ndjekur",
-  "lists.replies_policy.list_replies": "Anëtarë të listës",
-  "lists.replies_policy.no_replies": "Askush",
+  "lists.replies_policy.followed": "Cilido përdorues i ndjekur",
+  "lists.replies_policy.list": "Anëtarë të listës",
+  "lists.replies_policy.none": "Askush",
   "lists.replies_policy.title": "Shfaq përgjigje për:",
   "lists.search": "Kërkoni mes personash që ndiqni",
   "lists.subheading": "Listat tuaja",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Përmendje",
   "notifications.filter.polls": "Përfundime pyetësori",
   "notifications.filter.statuses": "Përditësime prej personash që ndiqni",
+  "notifications.grant_permission": "Akordoji leje.",
   "notifications.group": "{count}s njoftime",
   "notifications.mark_as_read": "Vëri shenjë çdo njoftimi si të lexuar",
   "notifications.permission_denied": "S’mund të aktivizohen njoftime në desktop, ngaqë janë mohuar lejet për këtë.",
   "notifications.permission_denied_alert": "S’mund të aktivizohen njoftimet në desktop, ngaqë lejet e shfletuesit për këtë janë mohuar më herët",
+  "notifications.permission_required": "S’merren dot njoftime desktop, ngaqë s’është akorduar leja përkatëse.",
   "notifications_permission_banner.enable": "Aktivizo njoftime në desktop",
   "notifications_permission_banner.how_to_control": "Për të marrë njoftime, kur Mastodon-i s’është i hapur, aktivizoni njoftime në desktop. Përmes butoni {icon} më sipër, mund të kontrolloni me përpikëri cilat lloje ndërveprimesh prodhojnë njoftime në dekstop, pasi të jenë aktivizuar.",
   "notifications_permission_banner.title": "Mos t’ju shpëtojë gjë",
diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json
index 38fc9cdce..88c9df59b 100644
--- a/app/javascript/mastodon/locales/sr-Latn.json
+++ b/app/javascript/mastodon/locales/sr-Latn.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Rezultati pretrage",
   "emoji_button.symbols": "Simboli",
   "emoji_button.travel": "Putovanja & mesta",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Sledeći",
   "lightbox.previous": "Prethodni",
-  "lightbox.view_context": "View context",
   "lists.account.add": "Dodaj na listu",
   "lists.account.remove": "Ukloni sa liste",
   "lists.delete": "Obriši listu",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Change title",
   "lists.new.create": "Dodaj listu",
   "lists.new.title_placeholder": "Naslov nove liste",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Pretraži među ljudima koje pratite",
   "lists.subheading": "Vaše liste",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mentions",
   "notifications.filter.polls": "Poll results",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} notifications",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json
index 8df81fae6..2509c9085 100644
--- a/app/javascript/mastodon/locales/sr.json
+++ b/app/javascript/mastodon/locales/sr.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Резултати претраге",
   "emoji_button.symbols": "Симболи",
   "emoji_button.travel": "Путовања и места",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "Овде нема труба!",
   "empty_column.account_unavailable": "Профил недоступан",
   "empty_column.blocks": "Још увек немате блокираних корисника.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Следећи",
   "lightbox.previous": "Претходни",
-  "lightbox.view_context": "View context",
   "lists.account.add": "Додај на листу",
   "lists.account.remove": "Уклони са листе",
   "lists.delete": "Обриши листу",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Change title",
   "lists.new.create": "Додај листу",
   "lists.new.title_placeholder": "Наслов нове листе",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Претражи међу људима које пратите",
   "lists.subheading": "Ваше листе",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mentions",
   "notifications.filter.polls": "Poll results",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} обавештења",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json
index 86f4738c7..a0fc4a4f3 100644
--- a/app/javascript/mastodon/locales/sv.json
+++ b/app/javascript/mastodon/locales/sv.json
@@ -9,10 +9,10 @@
   "account.browse_more_on_origin_server": "Läs mer på original profilen",
   "account.cancel_follow_request": "Avbryt följarförfrågan",
   "account.direct": "Skicka ett direktmeddelande till @{name}",
-  "account.disable_notifications": "Stop notifying me when @{name} posts",
+  "account.disable_notifications": "Sluta meddela mig när @{name} tutar",
   "account.domain_blocked": "Domän dold",
   "account.edit_profile": "Redigera profil",
-  "account.enable_notifications": "Notify me when @{name} posts",
+  "account.enable_notifications": "Meddela mig när @{name} tutar",
   "account.endorse": "Visa på profil",
   "account.follow": "Följ",
   "account.followers": "Följare",
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Sökresultat",
   "emoji_button.symbols": "Symboler",
   "emoji_button.travel": "Resor & platser",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "Inga inlägg här!",
   "empty_column.account_unavailable": "Profilen ej tillgänglig",
   "empty_column.blocks": "Du har ännu ej blockerat några användare.",
@@ -168,9 +169,9 @@
   "empty_column.notifications": "Du har inga meddelanden än. Interagera med andra för att starta konversationen.",
   "empty_column.public": "Det finns inget här! Skriv något offentligt, eller följ manuellt användarna från andra instanser för att fylla på det",
   "error.unexpected_crash.explanation": "På grund av en bugg i vår kod eller kompatiblitetsproblem i webbläsaren kan den här sidan inte visas korrekt.",
-  "error.unexpected_crash.explanation_addons": "This page could not be displayed correctly. This error is likely caused by a browser add-on or automatic translation tools.",
+  "error.unexpected_crash.explanation_addons": "Denna sida kunde inte visas korrekt. Detta beror troligen på ett webbläsartillägg eller ett automatiskt översättningsverktyg.",
   "error.unexpected_crash.next_steps": "Prova att ladda om sidan. Om det inte hjälper kan du försöka använda Mastodon med en annan webbläsare eller app.",
-  "error.unexpected_crash.next_steps_addons": "Try disabling them and refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.",
+  "error.unexpected_crash.next_steps_addons": "Prova att avaktivera dem och uppdatera sidan. Om detta inte hjälper kan du försöka använda Mastodon med en annan webbläsare eller en app.",
   "errors.unexpected_crash.copy_stacktrace": "Kopiera stacktrace till urklipp",
   "errors.unexpected_crash.report_issue": "Rapportera problem",
   "follow_request.authorize": "Godkänn",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Nästa",
   "lightbox.previous": "Tidigare",
-  "lightbox.view_context": "Visa kontext",
   "lists.account.add": "Lägg till i lista",
   "lists.account.remove": "Ta bort från lista",
   "lists.delete": "Radera lista",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Ändra titel",
   "lists.new.create": "Lägg till lista",
   "lists.new.title_placeholder": "Ny listrubrik",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Sök bland personer du följer",
   "lists.subheading": "Dina listor",
@@ -334,12 +334,14 @@
   "notifications.filter.mentions": "Omnämningar",
   "notifications.filter.polls": "Omröstningsresultat",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} aviseringar",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
-  "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": "För att ta emot aviseringar när Mastodon inte är öppet, aktivera skrivbordsaviseringar. När de är aktiverade kan du styra exakt vilka typer av interaktioner som aviseras via {icon} -knappen ovan.",
   "notifications_permission_banner.title": "Never miss a thing",
   "picture_in_picture.restore": "Put it back",
   "poll.closed": "Stängd",
diff --git a/app/javascript/mastodon/locales/szl.json b/app/javascript/mastodon/locales/szl.json
index 17ffe5519..70f6ab152 100644
--- a/app/javascript/mastodon/locales/szl.json
+++ b/app/javascript/mastodon/locales/szl.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Search results",
   "emoji_button.symbols": "Symbols",
   "emoji_button.travel": "Travel & Places",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Next",
   "lightbox.previous": "Previous",
-  "lightbox.view_context": "View context",
   "lists.account.add": "Add to list",
   "lists.account.remove": "Remove from list",
   "lists.delete": "Delete list",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Change title",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mentions",
   "notifications.filter.polls": "Poll results",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} notifications",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/ta.json b/app/javascript/mastodon/locales/ta.json
index d509b619a..8f5f06cc4 100644
--- a/app/javascript/mastodon/locales/ta.json
+++ b/app/javascript/mastodon/locales/ta.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "தேடல் முடிவுகள்",
   "emoji_button.symbols": "குறியீடுகள்",
   "emoji_button.travel": "சுற்றுலா மற்றும் இடங்கள்",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "டூட்டுகள் ஏதும் இல்லை!",
   "empty_column.account_unavailable": "சுயவிவரம் கிடைக்கவில்லை",
   "empty_column.blocks": "நீங்கள் இதுவரை எந்தப் பயனர்களையும் முடக்கியிருக்கவில்லை.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "அடுத்த",
   "lightbox.previous": "சென்ற",
-  "lightbox.view_context": "சூழலைக் பார்",
   "lists.account.add": "பட்டியலில் சேர்",
   "lists.account.remove": "பட்டியலில் இருந்து அகற்று",
   "lists.delete": "பட்டியலை நீக்கு",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "தலைப்பு மாற்றவும்",
   "lists.new.create": "பட்டியலில் சேர்",
   "lists.new.title_placeholder": "புதிய பட்டியல் தலைப்பு",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "நீங்கள் பின்தொடரும் நபர்கள் மத்தியில் தேடுதல்",
   "lists.subheading": "உங்கள் பட்டியல்கள்",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "குறிப்பிடுகிறார்",
   "notifications.filter.polls": "கருத்துக்கணிப்பு முடிவுகள்",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} அறிவிப்புகள்",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/tai.json b/app/javascript/mastodon/locales/tai.json
index 17ffe5519..70f6ab152 100644
--- a/app/javascript/mastodon/locales/tai.json
+++ b/app/javascript/mastodon/locales/tai.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Search results",
   "emoji_button.symbols": "Symbols",
   "emoji_button.travel": "Travel & Places",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Next",
   "lightbox.previous": "Previous",
-  "lightbox.view_context": "View context",
   "lists.account.add": "Add to list",
   "lists.account.remove": "Remove from list",
   "lists.delete": "Delete list",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Change title",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mentions",
   "notifications.filter.polls": "Poll results",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} notifications",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/te.json b/app/javascript/mastodon/locales/te.json
index 84e73f7e5..994682a67 100644
--- a/app/javascript/mastodon/locales/te.json
+++ b/app/javascript/mastodon/locales/te.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "శోధన ఫలితాలు",
   "emoji_button.symbols": "చిహ్నాలు",
   "emoji_button.travel": "ప్రయాణం & ప్రదేశాలు",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "ఇక్కడ ఏ టూట్లూ లేవు!No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "మీరు ఇంకా ఏ వినియోగదారులనూ బ్లాక్ చేయలేదు.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "తరువాత",
   "lightbox.previous": "మునుపటి",
-  "lightbox.view_context": "View context",
   "lists.account.add": "జాబితాకు జోడించు",
   "lists.account.remove": "జాబితా నుండి తొలగించు",
   "lists.delete": "జాబితాను తొలగించు",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "శీర్షిక మార్చు",
   "lists.new.create": "జాబితాను జోడించు",
   "lists.new.title_placeholder": "కొత్త జాబితా శీర్షిక",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "మీరు అనుసరించే వ్యక్తులలో శోధించండి",
   "lists.subheading": "మీ జాబితాలు",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "పేర్కొన్నవి",
   "notifications.filter.polls": "ఎన్నిక ఫలితాలు",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} ప్రకటనలు",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json
index a2add6715..30ec26809 100644
--- a/app/javascript/mastodon/locales/th.json
+++ b/app/javascript/mastodon/locales/th.json
@@ -98,9 +98,9 @@
   "compose_form.poll.switch_to_single": "เปลี่ยนการสำรวจความคิดเห็นเป็นอนุญาตตัวเลือกเดี่ยว",
   "compose_form.publish": "โพสต์",
   "compose_form.publish_loud": "{publish}!",
-  "compose_form.sensitive.hide": "ทำเครื่องหมายสื่อว่าละเอียดอ่อน",
-  "compose_form.sensitive.marked": "มีการทำเครื่องหมายสื่อว่าละเอียดอ่อน",
-  "compose_form.sensitive.unmarked": "ไม่มีการทำเครื่องหมายสื่อว่าละเอียดอ่อน",
+  "compose_form.sensitive.hide": "{count, plural, other {ทำเครื่องหมายสื่อว่าละเอียดอ่อน}}",
+  "compose_form.sensitive.marked": "{count, plural, other {มีการทำเครื่องหมายสื่อว่าละเอียดอ่อน}}",
+  "compose_form.sensitive.unmarked": "{count, plural, other {ไม่มีการทำเครื่องหมายสื่อว่าละเอียดอ่อน}}",
   "compose_form.spoiler.marked": "มีการซ่อนข้อความอยู่หลังคำเตือน",
   "compose_form.spoiler.unmarked": "ไม่มีการซ่อนข้อความ",
   "compose_form.spoiler_placeholder": "เขียนคำเตือนของคุณที่นี่",
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "ผลลัพธ์การค้นหา",
   "emoji_button.symbols": "สัญลักษณ์",
   "emoji_button.travel": "การเดินทางและสถานที่",
+  "empty_column.account_suspended": "ระงับบัญชีอยู่",
   "empty_column.account_timeline": "ไม่มีโพสต์ที่นี่!",
   "empty_column.account_unavailable": "ไม่มีโปรไฟล์",
   "empty_column.blocks": "คุณยังไม่ได้ปิดกั้นผู้ใช้ใด ๆ",
@@ -258,7 +259,6 @@
   "lightbox.expand": "ขยายกล่องดูภาพ",
   "lightbox.next": "ถัดไป",
   "lightbox.previous": "ก่อนหน้า",
-  "lightbox.view_context": "ดูบริบท",
   "lists.account.add": "เพิ่มไปยังรายการ",
   "lists.account.remove": "เอาออกจากรายการ",
   "lists.delete": "ลบรายการ",
@@ -266,15 +266,15 @@
   "lists.edit.submit": "เปลี่ยนชื่อเรื่อง",
   "lists.new.create": "เพิ่มรายการ",
   "lists.new.title_placeholder": "ชื่อเรื่องรายการใหม่",
-  "lists.replies_policy.all_replies": "ผู้ใช้ใด ๆ ที่ติดตาม",
-  "lists.replies_policy.list_replies": "สมาชิกของรายการ",
-  "lists.replies_policy.no_replies": "ไม่มีใคร",
+  "lists.replies_policy.followed": "ผู้ใช้ใด ๆ ที่ติดตาม",
+  "lists.replies_policy.list": "สมาชิกของรายการ",
+  "lists.replies_policy.none": "ไม่มีใคร",
   "lists.replies_policy.title": "แสดงการตอบกลับแก่:",
   "lists.search": "ค้นหาในหมู่ผู้คนที่คุณติดตาม",
   "lists.subheading": "รายการของคุณ",
   "load_pending": "{count, plural, other {# รายการใหม่}}",
   "loading_indicator.label": "กำลังโหลด...",
-  "media_gallery.toggle_visible": "ซ่อน{number, plural, other {ภาพ}}",
+  "media_gallery.toggle_visible": "ซ่อน {number, plural, other {ภาพ}}",
   "missing_indicator.label": "ไม่พบ",
   "missing_indicator.sublabel": "ไม่พบทรัพยากรนี้",
   "mute_modal.duration": "ระยะเวลา",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "การกล่าวถึง",
   "notifications.filter.polls": "ผลลัพธ์การสำรวจความคิดเห็น",
   "notifications.filter.statuses": "การอัปเดตจากผู้คนที่คุณติดตาม",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} การแจ้งเตือน",
   "notifications.mark_as_read": "ทำเครื่องหมายทุกการแจ้งเตือนว่าอ่านแล้ว",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "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.title": "ไม่พลาดสิ่งต่าง ๆ",
diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json
index ec5f65a0c..1d56d169c 100644
--- a/app/javascript/mastodon/locales/tr.json
+++ b/app/javascript/mastodon/locales/tr.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Arama sonuçları",
   "emoji_button.symbols": "Semboller",
   "emoji_button.travel": "Seyahat ve Yerler",
+  "empty_column.account_suspended": "Hesap askıya alındı",
   "empty_column.account_timeline": "Burada hiç toot yok!",
   "empty_column.account_unavailable": "Profil kullanılamıyor",
   "empty_column.blocks": "Henüz bir kullanıcıyı engellemediniz.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Resim görüntüleme kutusunu genişlet",
   "lightbox.next": "Sonraki",
   "lightbox.previous": "Önceki",
-  "lightbox.view_context": "İçeriği görüntüle",
   "lists.account.add": "Listeye ekle",
   "lists.account.remove": "Listeden kaldır",
   "lists.delete": "Listeyi sil",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Başlığı değiştir",
   "lists.new.create": "Liste ekle",
   "lists.new.title_placeholder": "Yeni liste başlığı",
-  "lists.replies_policy.all_replies": "Takip edilen herhangi bir kullanıcı",
-  "lists.replies_policy.list_replies": "Listenin üyeleri",
-  "lists.replies_policy.no_replies": "Hiç kimse",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Listenin üyeleri",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Yanıtları göster:",
   "lists.search": "Takip ettiğiniz kişiler arasından arayın",
   "lists.subheading": "Listeleriniz",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Bahsetmeler",
   "notifications.filter.polls": "Anket sonuçları",
   "notifications.filter.statuses": "Takip ettiğiniz kişilerden gelen güncellemeler",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} bildirim",
   "notifications.mark_as_read": "Her bildirimi okundu olarak işaretle",
   "notifications.permission_denied": "Daha önce reddedilen tarayıcı izinleri isteği nedeniyle masaüstü bildirimleri kullanılamıyor",
   "notifications.permission_denied_alert": "Tarayıcı izni daha önce reddedildiğinden, masaüstü bildirimleri etkinleştirilemez",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Masaüstü bildirimlerini etkinleştir",
   "notifications_permission_banner.how_to_control": "Mastodon açık olmadığında bildirim almak için masaüstü bildirimlerini etkinleştirin. Etkinleştirildikten sonra yukarıdaki {icon} düğmesini kullanarak hangi etkileşim türlerinin masaüstü bildirimleri oluşturduğunu tam olarak kontrol edebilirsiniz.",
   "notifications_permission_banner.title": "Hiçbir şeyi kaçırmayın",
diff --git a/app/javascript/mastodon/locales/tt.json b/app/javascript/mastodon/locales/tt.json
new file mode 100644
index 000000000..224c51301
--- /dev/null
+++ b/app/javascript/mastodon/locales/tt.json
@@ -0,0 +1,486 @@
+{
+  "account.account_note_header": "Язма",
+  "account.add_or_remove_from_list": "Исемлеккә кертү я бетерү",
+  "account.badges.bot": "Бот",
+  "account.badges.group": "Төркем",
+  "account.block": "Block @{name}",
+  "account.block_domain": "Block domain {domain}",
+  "account.blocked": "Blocked",
+  "account.browse_more_on_origin_server": "Тулырак оригинал профилендә карап була",
+  "account.cancel_follow_request": "Cancel follow request",
+  "account.direct": "Direct message @{name}",
+  "account.disable_notifications": "Stop notifying me when @{name} posts",
+  "account.domain_blocked": "Domain blocked",
+  "account.edit_profile": "Edit profile",
+  "account.enable_notifications": "Notify me when @{name} posts",
+  "account.endorse": "Feature on profile",
+  "account.follow": "Follow",
+  "account.followers": "Followers",
+  "account.followers.empty": "No one follows this user yet.",
+  "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
+  "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
+  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows_you": "Follows you",
+  "account.hide_reblogs": "Hide boosts from @{name}",
+  "account.last_status": "Last active",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
+  "account.media": "Media",
+  "account.mention": "Mention @{name}",
+  "account.moved_to": "{name} has moved to:",
+  "account.mute": "Mute @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
+  "account.muted": "Muted",
+  "account.never_active": "Never",
+  "account.posts": "Toots",
+  "account.posts_with_replies": "Toots and replies",
+  "account.report": "Report @{name}",
+  "account.requested": "Awaiting approval",
+  "account.share": "Share @{name}'s profile",
+  "account.show_reblogs": "Show boosts from @{name}",
+  "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}",
+  "account.unblock": "Unblock @{name}",
+  "account.unblock_domain": "Unblock domain {domain}",
+  "account.unendorse": "Don't feature on profile",
+  "account.unfollow": "Unfollow",
+  "account.unmute": "Unmute @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
+  "account_note.placeholder": "Click to add a note",
+  "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.",
+  "alert.rate_limited.title": "Rate limited",
+  "alert.unexpected.message": "An unexpected error occurred.",
+  "alert.unexpected.title": "Oops!",
+  "announcement.announcement": "Announcement",
+  "autosuggest_hashtag.per_week": "{count} per week",
+  "boost_modal.combo": "You can press {combo} to skip this next time",
+  "bundle_column_error.body": "Something went wrong while loading this component.",
+  "bundle_column_error.retry": "Try again",
+  "bundle_column_error.title": "Network error",
+  "bundle_modal_error.close": "Close",
+  "bundle_modal_error.message": "Something went wrong while loading this component.",
+  "bundle_modal_error.retry": "Try again",
+  "column.blocks": "Blocked users",
+  "column.bookmarks": "Bookmarks",
+  "column.community": "Local timeline",
+  "column.direct": "Direct messages",
+  "column.directory": "Browse profiles",
+  "column.domain_blocks": "Blocked domains",
+  "column.favourites": "Favourites",
+  "column.follow_requests": "Follow requests",
+  "column.home": "Home",
+  "column.lists": "Lists",
+  "column.mutes": "Muted users",
+  "column.notifications": "Notifications",
+  "column.pins": "Pinned toot",
+  "column.public": "Federated timeline",
+  "column_back_button.label": "Back",
+  "column_header.hide_settings": "Hide settings",
+  "column_header.moveLeft_settings": "Move column to the left",
+  "column_header.moveRight_settings": "Move column to the right",
+  "column_header.pin": "Pin",
+  "column_header.show_settings": "Show settings",
+  "column_header.unpin": "Unpin",
+  "column_subheading.settings": "Settings",
+  "community.column_settings.local_only": "Local only",
+  "community.column_settings.media_only": "Media only",
+  "community.column_settings.remote_only": "Remote only",
+  "compose_form.direct_message_warning": "This toot will only be sent to all the mentioned users.",
+  "compose_form.direct_message_warning_learn_more": "Learn more",
+  "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
+  "compose_form.placeholder": "What is on your mind?",
+  "compose_form.poll.add_option": "Add a choice",
+  "compose_form.poll.duration": "Poll duration",
+  "compose_form.poll.option_placeholder": "Choice {number}",
+  "compose_form.poll.remove_option": "Remove this choice",
+  "compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices",
+  "compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
+  "compose_form.publish": "Toot",
+  "compose_form.publish_loud": "{publish}!",
+  "compose_form.sensitive.hide": "{count, plural, one {Mark media as sensitive} other {Mark media as sensitive}}",
+  "compose_form.sensitive.marked": "{count, plural, one {Media is marked as sensitive} other {Media is marked as sensitive}}",
+  "compose_form.sensitive.unmarked": "{count, plural, one {Media is not marked as sensitive} other {Media is not marked as sensitive}}",
+  "compose_form.spoiler.marked": "Text is hidden behind warning",
+  "compose_form.spoiler.unmarked": "Text is not hidden",
+  "compose_form.spoiler_placeholder": "Write your warning here",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.block_and_report": "Block & Report",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.delete_list.confirm": "Delete",
+  "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.",
+  "confirmations.logout.confirm": "Log out",
+  "confirmations.logout.message": "Are you sure you want to log out?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
+  "confirmations.redraft.confirm": "Delete & redraft",
+  "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.",
+  "confirmations.reply.confirm": "Reply",
+  "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
+  "confirmations.unfollow.confirm": "Unfollow",
+  "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
+  "conversation.delete": "Delete conversation",
+  "conversation.mark_as_read": "Mark as read",
+  "conversation.open": "View conversation",
+  "conversation.with": "With {names}",
+  "directory.federated": "From known fediverse",
+  "directory.local": "From {domain} only",
+  "directory.new_arrivals": "New arrivals",
+  "directory.recently_active": "Recently active",
+  "embed.instructions": "Embed this status on your website by copying the code below.",
+  "embed.preview": "Here is what it will look like:",
+  "emoji_button.activity": "Activity",
+  "emoji_button.custom": "Custom",
+  "emoji_button.flags": "Flags",
+  "emoji_button.food": "Food & Drink",
+  "emoji_button.label": "Insert emoji",
+  "emoji_button.nature": "Nature",
+  "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
+  "emoji_button.objects": "Objects",
+  "emoji_button.people": "People",
+  "emoji_button.recent": "Frequently used",
+  "emoji_button.search": "Search...",
+  "emoji_button.search_results": "Search results",
+  "emoji_button.symbols": "Symbols",
+  "emoji_button.travel": "Travel & Places",
+  "empty_column.account_suspended": "Account suspended",
+  "empty_column.account_timeline": "No toots here!",
+  "empty_column.account_unavailable": "Profile unavailable",
+  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.bookmarked_statuses": "You don't have any bookmarked toots 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.domain_blocks": "There are no blocked domains yet.",
+  "empty_column.favourited_statuses": "You don't have any favourite toots yet. When you favourite one, it will show up here.",
+  "empty_column.favourites": "No one has favourited this toot yet. When someone does, they will show up here.",
+  "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.hashtag": "There is nothing in this hashtag yet.",
+  "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
+  "empty_column.home.public_timeline": "the public timeline",
+  "empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.",
+  "empty_column.lists": "You don't have any lists yet. When you create one, it will show up here.",
+  "empty_column.mutes": "You haven't muted any users yet.",
+  "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
+  "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other servers to fill it up",
+  "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.",
+  "error.unexpected_crash.explanation_addons": "This page could not be displayed correctly. This error is likely caused by a browser add-on or automatic translation tools.",
+  "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.",
+  "error.unexpected_crash.next_steps_addons": "Try disabling them and refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.",
+  "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard",
+  "errors.unexpected_crash.report_issue": "Report issue",
+  "follow_request.authorize": "Authorize",
+  "follow_request.reject": "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.",
+  "generic.saved": "Saved",
+  "getting_started.developers": "Developers",
+  "getting_started.directory": "Profile directory",
+  "getting_started.documentation": "Documentation",
+  "getting_started.heading": "Getting started",
+  "getting_started.invite": "Invite people",
+  "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}.",
+  "getting_started.security": "Security",
+  "getting_started.terms": "Terms of service",
+  "hashtag.column_header.tag_mode.all": "and {additional}",
+  "hashtag.column_header.tag_mode.any": "or {additional}",
+  "hashtag.column_header.tag_mode.none": "without {additional}",
+  "hashtag.column_settings.select.no_options_message": "No suggestions found",
+  "hashtag.column_settings.select.placeholder": "Enter hashtags…",
+  "hashtag.column_settings.tag_mode.all": "All of these",
+  "hashtag.column_settings.tag_mode.any": "Any of these",
+  "hashtag.column_settings.tag_mode.none": "None of these",
+  "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
+  "home.column_settings.basic": "Basic",
+  "home.column_settings.show_reblogs": "Show boosts",
+  "home.column_settings.show_replies": "Show replies",
+  "home.hide_announcements": "Hide announcements",
+  "home.show_announcements": "Show announcements",
+  "intervals.full.days": "{number, plural, one {# day} other {# days}}",
+  "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
+  "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}",
+  "introduction.federation.action": "Next",
+  "introduction.federation.federated.headline": "Federated",
+  "introduction.federation.federated.text": "Public posts from other servers of the fediverse will appear in the federated timeline.",
+  "introduction.federation.home.headline": "Home",
+  "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!",
+  "introduction.federation.local.headline": "Local",
+  "introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
+  "introduction.interactions.action": "Finish toot-orial!",
+  "introduction.interactions.favourite.headline": "Favourite",
+  "introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
+  "introduction.interactions.reblog.headline": "Boost",
+  "introduction.interactions.reblog.text": "You can share other people's toots with your followers by boosting them.",
+  "introduction.interactions.reply.headline": "Reply",
+  "introduction.interactions.reply.text": "You can reply to other people's and your own toots, which will chain them together in a conversation.",
+  "introduction.welcome.action": "Let's go!",
+  "introduction.welcome.headline": "First steps",
+  "introduction.welcome.text": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.",
+  "keyboard_shortcuts.back": "to navigate back",
+  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.boost": "to boost",
+  "keyboard_shortcuts.column": "to focus a status in one of the columns",
+  "keyboard_shortcuts.compose": "to focus the compose textarea",
+  "keyboard_shortcuts.description": "Description",
+  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.down": "to move down in the list",
+  "keyboard_shortcuts.enter": "to open status",
+  "keyboard_shortcuts.favourite": "to favourite",
+  "keyboard_shortcuts.favourites": "to open favourites list",
+  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.heading": "Keyboard Shortcuts",
+  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.hotkey": "Hotkey",
+  "keyboard_shortcuts.legend": "to display this legend",
+  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.mention": "to mention author",
+  "keyboard_shortcuts.muted": "to open muted users list",
+  "keyboard_shortcuts.my_profile": "to open your profile",
+  "keyboard_shortcuts.notifications": "to open notifications column",
+  "keyboard_shortcuts.open_media": "to open media",
+  "keyboard_shortcuts.pinned": "to open pinned toots list",
+  "keyboard_shortcuts.profile": "to open author's profile",
+  "keyboard_shortcuts.reply": "to reply",
+  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.search": "to focus search",
+  "keyboard_shortcuts.spoilers": "to show/hide CW field",
+  "keyboard_shortcuts.start": "to open \"get started\" column",
+  "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW",
+  "keyboard_shortcuts.toggle_sensitivity": "to show/hide media",
+  "keyboard_shortcuts.toot": "to start a brand new toot",
+  "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
+  "keyboard_shortcuts.up": "to move up in the list",
+  "lightbox.close": "Close",
+  "lightbox.compress": "Compress image view box",
+  "lightbox.expand": "Expand image view box",
+  "lightbox.next": "Next",
+  "lightbox.previous": "Previous",
+  "lists.account.add": "Add to list",
+  "lists.account.remove": "Remove from list",
+  "lists.delete": "Delete list",
+  "lists.edit": "Edit list",
+  "lists.edit.submit": "Change title",
+  "lists.new.create": "Add list",
+  "lists.new.title_placeholder": "New list title",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
+  "lists.replies_policy.title": "Show replies to:",
+  "lists.search": "Search among people you follow",
+  "lists.subheading": "Your lists",
+  "load_pending": "{count, plural, one {# new item} other {# new items}}",
+  "loading_indicator.label": "Loading...",
+  "media_gallery.toggle_visible": "Hide {number, plural, one {image} other {images}}",
+  "missing_indicator.label": "Not found",
+  "missing_indicator.sublabel": "This resource could not be found",
+  "mute_modal.duration": "Duration",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
+  "mute_modal.indefinite": "Indefinite",
+  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.blocks": "Blocked users",
+  "navigation_bar.bookmarks": "Bookmarks",
+  "navigation_bar.community_timeline": "Local timeline",
+  "navigation_bar.compose": "Compose new toot",
+  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.discover": "Discover",
+  "navigation_bar.domain_blocks": "Hidden domains",
+  "navigation_bar.edit_profile": "Edit profile",
+  "navigation_bar.favourites": "Favourites",
+  "navigation_bar.filters": "Muted words",
+  "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.follows_and_followers": "Follows and followers",
+  "navigation_bar.info": "About this server",
+  "navigation_bar.keyboard_shortcuts": "Hotkeys",
+  "navigation_bar.lists": "Lists",
+  "navigation_bar.logout": "Logout",
+  "navigation_bar.mutes": "Muted users",
+  "navigation_bar.personal": "Personal",
+  "navigation_bar.pins": "Pinned toots",
+  "navigation_bar.preferences": "Preferences",
+  "navigation_bar.public_timeline": "Federated timeline",
+  "navigation_bar.security": "Security",
+  "notification.favourite": "{name} favourited your status",
+  "notification.follow": "{name} followed you",
+  "notification.follow_request": "{name} has requested to follow you",
+  "notification.mention": "{name} mentioned you",
+  "notification.own_poll": "Your poll has ended",
+  "notification.poll": "A poll you have voted in has ended",
+  "notification.reblog": "{name} boosted your status",
+  "notification.status": "{name} just posted",
+  "notifications.clear": "Clear notifications",
+  "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
+  "notifications.column_settings.alert": "Desktop notifications",
+  "notifications.column_settings.favourite": "Favourites:",
+  "notifications.column_settings.filter_bar.advanced": "Display all categories",
+  "notifications.column_settings.filter_bar.category": "Quick filter bar",
+  "notifications.column_settings.filter_bar.show": "Show",
+  "notifications.column_settings.follow": "New followers:",
+  "notifications.column_settings.follow_request": "New follow requests:",
+  "notifications.column_settings.mention": "Mentions:",
+  "notifications.column_settings.poll": "Poll results:",
+  "notifications.column_settings.push": "Push notifications",
+  "notifications.column_settings.reblog": "Boosts:",
+  "notifications.column_settings.show": "Show in column",
+  "notifications.column_settings.sound": "Play sound",
+  "notifications.column_settings.status": "New toots:",
+  "notifications.filter.all": "All",
+  "notifications.filter.boosts": "Boosts",
+  "notifications.filter.favourites": "Favourites",
+  "notifications.filter.follows": "Follows",
+  "notifications.filter.mentions": "Mentions",
+  "notifications.filter.polls": "Poll results",
+  "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
+  "notifications.group": "{count} notifications",
+  "notifications.mark_as_read": "Mark every notification as read",
+  "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
+  "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
+  "notifications_permission_banner.enable": "Enable desktop notifications",
+  "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.title": "Never miss a thing",
+  "picture_in_picture.restore": "Put it back",
+  "poll.closed": "Closed",
+  "poll.refresh": "Refresh",
+  "poll.total_people": "{count, plural, one {# person} other {# people}}",
+  "poll.total_votes": "{count, plural, one {# vote} other {# votes}}",
+  "poll.vote": "Vote",
+  "poll.voted": "You voted for this answer",
+  "poll_button.add_poll": "Add a poll",
+  "poll_button.remove_poll": "Remove poll",
+  "privacy.change": "Adjust status privacy",
+  "privacy.direct.long": "Visible for mentioned users only",
+  "privacy.direct.short": "Direct",
+  "privacy.private.long": "Visible for followers only",
+  "privacy.private.short": "Followers-only",
+  "privacy.public.long": "Visible for all, shown in public timelines",
+  "privacy.public.short": "Public",
+  "privacy.unlisted.long": "Visible for all, but not in public timelines",
+  "privacy.unlisted.short": "Unlisted",
+  "refresh": "Refresh",
+  "regeneration_indicator.label": "Loading…",
+  "regeneration_indicator.sublabel": "Your home feed is being prepared!",
+  "relative_time.days": "{number}d",
+  "relative_time.hours": "{number}h",
+  "relative_time.just_now": "now",
+  "relative_time.minutes": "{number}m",
+  "relative_time.seconds": "{number}s",
+  "relative_time.today": "today",
+  "reply_indicator.cancel": "Cancel",
+  "report.forward": "Forward to {target}",
+  "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+  "report.hint": "The report will be sent to your server moderators. You can provide an explanation of why you are reporting this account below:",
+  "report.placeholder": "Additional comments",
+  "report.submit": "Submit",
+  "report.target": "Report {target}",
+  "search.placeholder": "Search",
+  "search_popout.search_format": "Advanced search format",
+  "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": "hashtag",
+  "search_popout.tips.status": "status",
+  "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
+  "search_popout.tips.user": "user",
+  "search_results.accounts": "People",
+  "search_results.hashtags": "Hashtags",
+  "search_results.statuses": "Toots",
+  "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.",
+  "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
+  "status.admin_account": "Open moderation interface for @{name}",
+  "status.admin_status": "Open this status in the moderation interface",
+  "status.block": "Block @{name}",
+  "status.bookmark": "Bookmark",
+  "status.cancel_reblog_private": "Unboost",
+  "status.cannot_reblog": "This post cannot be boosted",
+  "status.copy": "Copy link to status",
+  "status.delete": "Delete",
+  "status.detailed_status": "Detailed conversation view",
+  "status.direct": "Direct message @{name}",
+  "status.embed": "Embed",
+  "status.favourite": "Favourite",
+  "status.filtered": "Filtered",
+  "status.load_more": "Load more",
+  "status.media_hidden": "Media hidden",
+  "status.mention": "Mention @{name}",
+  "status.more": "More",
+  "status.mute": "Mute @{name}",
+  "status.mute_conversation": "Mute conversation",
+  "status.open": "Expand this status",
+  "status.pin": "Pin on profile",
+  "status.pinned": "Pinned toot",
+  "status.read_more": "Read more",
+  "status.reblog": "Boost",
+  "status.reblog_private": "Boost with original visibility",
+  "status.reblogged_by": "{name} boosted",
+  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
+  "status.redraft": "Delete & re-draft",
+  "status.remove_bookmark": "Remove bookmark",
+  "status.reply": "Reply",
+  "status.replyAll": "Reply to thread",
+  "status.report": "Report @{name}",
+  "status.sensitive_warning": "Sensitive content",
+  "status.share": "Share",
+  "status.show_less": "Show less",
+  "status.show_less_all": "Show less for all",
+  "status.show_more": "Show more",
+  "status.show_more_all": "Show more for all",
+  "status.show_thread": "Show thread",
+  "status.uncached_media_warning": "Not available",
+  "status.unmute_conversation": "Unmute conversation",
+  "status.unpin": "Unpin from profile",
+  "suggestions.dismiss": "Dismiss suggestion",
+  "suggestions.header": "You might be interested in…",
+  "tabs_bar.federated_timeline": "Federated",
+  "tabs_bar.home": "Home",
+  "tabs_bar.local_timeline": "Local",
+  "tabs_bar.notifications": "Notifications",
+  "tabs_bar.search": "Search",
+  "time_remaining.days": "{number, plural, one {# day} other {# days}} left",
+  "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left",
+  "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left",
+  "time_remaining.moments": "Moments remaining",
+  "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left",
+  "timeline_hint.remote_resource_not_displayed": "{resource} from other servers are not displayed.",
+  "timeline_hint.resources.followers": "Followers",
+  "timeline_hint.resources.follows": "Follows",
+  "timeline_hint.resources.statuses": "Older toots",
+  "trends.counter_by_accounts": "{count, plural, one {{counter} person} other {{counter} people}} talking",
+  "trends.trending_now": "Trending now",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
+  "units.short.billion": "{count}B",
+  "units.short.million": "{count}M",
+  "units.short.thousand": "{count}K",
+  "upload_area.title": "Drag & drop to upload",
+  "upload_button.label": "Add images, a video or an audio file",
+  "upload_error.limit": "File upload limit exceeded.",
+  "upload_error.poll": "File upload not allowed with polls.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
+  "upload_form.description": "Describe for the visually impaired",
+  "upload_form.edit": "Edit",
+  "upload_form.thumbnail": "Change thumbnail",
+  "upload_form.undo": "Delete",
+  "upload_form.video_description": "Describe for people with hearing loss or visual impairment",
+  "upload_modal.analyzing_picture": "Analyzing picture…",
+  "upload_modal.apply": "Apply",
+  "upload_modal.choose_image": "Choose image",
+  "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog",
+  "upload_modal.detect_text": "Detect text from picture",
+  "upload_modal.edit_media": "Edit media",
+  "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.",
+  "upload_modal.preparing_ocr": "Preparing OCR…",
+  "upload_modal.preview_label": "Preview ({ratio})",
+  "upload_progress.label": "Uploading…",
+  "video.close": "Close video",
+  "video.download": "Download file",
+  "video.exit_fullscreen": "Exit full screen",
+  "video.expand": "Expand video",
+  "video.fullscreen": "Full screen",
+  "video.hide": "Hide video",
+  "video.mute": "Mute sound",
+  "video.pause": "Pause",
+  "video.play": "Play",
+  "video.unmute": "Unmute sound"
+}
diff --git a/app/javascript/mastodon/locales/ug.json b/app/javascript/mastodon/locales/ug.json
index 17ffe5519..70f6ab152 100644
--- a/app/javascript/mastodon/locales/ug.json
+++ b/app/javascript/mastodon/locales/ug.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Search results",
   "emoji_button.symbols": "Symbols",
   "emoji_button.travel": "Travel & Places",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Next",
   "lightbox.previous": "Previous",
-  "lightbox.view_context": "View context",
   "lists.account.add": "Add to list",
   "lists.account.remove": "Remove from list",
   "lists.delete": "Delete list",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Change title",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mentions",
   "notifications.filter.polls": "Poll results",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} notifications",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json
index dab23f448..941e43c99 100644
--- a/app/javascript/mastodon/locales/uk.json
+++ b/app/javascript/mastodon/locales/uk.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Результати пошуку",
   "emoji_button.symbols": "Символи",
   "emoji_button.travel": "Подорожі",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "Тут дмухалок немає!",
   "empty_column.account_unavailable": "Профіль недоступний",
   "empty_column.blocks": "Ви ще не заблокували жодного користувача.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Далі",
   "lightbox.previous": "Назад",
-  "lightbox.view_context": "Переглянути контекст",
   "lists.account.add": "Додати до списку",
   "lists.account.remove": "Видалити зі списку",
   "lists.delete": "Видалити список",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Змінити назву",
   "lists.new.create": "Додати список",
   "lists.new.title_placeholder": "Нова назва списку",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Шукати серед людей, на яких ви підписані",
   "lists.subheading": "Ваші списки",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Згадки",
   "notifications.filter.polls": "Результати опитування",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} сповіщень",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/ur.json b/app/javascript/mastodon/locales/ur.json
index 3260a33cd..aaccf1d83 100644
--- a/app/javascript/mastodon/locales/ur.json
+++ b/app/javascript/mastodon/locales/ur.json
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Search results",
   "emoji_button.symbols": "Symbols",
   "emoji_button.travel": "Travel & Places",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "یہاں کوئی نوٹس نہیں ہیں!",
   "empty_column.account_unavailable": "مشخص دستیاب نہیں ہے",
   "empty_column.blocks": "آپ نے ابھی کسی صارف کو مسدود نہیں کیا ہے.",
@@ -258,7 +259,6 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Next",
   "lightbox.previous": "Previous",
-  "lightbox.view_context": "View context",
   "lists.account.add": "Add to list",
   "lists.account.remove": "Remove from list",
   "lists.delete": "Delete list",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Change title",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "Mentions",
   "notifications.filter.polls": "Poll results",
   "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
   "notifications.group": "{count} notifications",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json
index 3da7cf375..14c74830b 100644
--- a/app/javascript/mastodon/locales/vi.json
+++ b/app/javascript/mastodon/locales/vi.json
@@ -6,12 +6,12 @@
   "account.block": "Chặn @{name}",
   "account.block_domain": "Ẩn mọi thứ từ {domain}",
   "account.blocked": "Đã chặn",
-  "account.browse_more_on_origin_server": "Tìm những tài khoản có liên quan",
+  "account.browse_more_on_origin_server": "Truy cập trang của người này",
   "account.cancel_follow_request": "Hủy yêu cầu theo dõi",
   "account.direct": "Nhắn tin @{name}",
   "account.disable_notifications": "Không thông báo khi @{name} đăng tút",
-  "account.domain_blocked": "Đã chặn người dùng",
-  "account.edit_profile": "Giới thiệu bản thân",
+  "account.domain_blocked": "Người đã chặn",
+  "account.edit_profile": "Chỉnh sửa trang cá nhân",
   "account.enable_notifications": "Thông báo khi @{name} đăng tút",
   "account.endorse": "Vinh danh người này",
   "account.follow": "Theo dõi",
@@ -19,7 +19,7 @@
   "account.followers.empty": "Chưa có người theo dõi nào.",
   "account.followers_counter": "{count, plural, one {{counter} Người theo dõi} other {{counter} Người theo dõi}}",
   "account.following_counter": "{count, plural, one {{counter} Theo dõi} other {{counter} Theo dõi}}",
-  "account.follows.empty": "Người dùng này chưa theo dõi ai.",
+  "account.follows.empty": "Người này chưa theo dõi ai.",
   "account.follows_you": "Đang theo dõi bạn",
   "account.hide_reblogs": "Ẩn chia sẻ từ @{name}",
   "account.last_status": "Online",
@@ -38,7 +38,7 @@
   "account.requested": "Đang chờ chấp thuận. Nhấp vào đây để hủy yêu cầu theo dõi",
   "account.share": "Chia sẻ hồ sơ @{name}",
   "account.show_reblogs": "Hiện chia sẻ từ @{name}",
-  "account.statuses_counter": "{count, plural, other {{counter} Tút}}",
+  "account.statuses_counter": "{count, plural, one {{counter} Tút} other {{counter} Tút}}",
   "account.unblock": "Bỏ chặn @{name}",
   "account.unblock_domain": "Bỏ ẩn {domain}",
   "account.unendorse": "Ngưng vinh danh người này",
@@ -59,17 +59,17 @@
   "bundle_modal_error.close": "Đóng",
   "bundle_modal_error.message": "Đã có lỗi xảy ra trong khi tải nội dung này.",
   "bundle_modal_error.retry": "Thử lại",
-  "column.blocks": "Người dùng đã chặn",
-  "column.bookmarks": "Để dành đọc lại",
+  "column.blocks": "Người đã chặn",
+  "column.bookmarks": "Đã lưu",
   "column.community": "Máy chủ của bạn",
-  "column.direct": "Tin nhắn của bạn",
+  "column.direct": "Tin nhắn",
   "column.directory": "Tìm người cùng sở thích",
   "column.domain_blocks": "Máy chủ đã chặn",
-  "column.favourites": "Lượt thích của bạn",
+  "column.favourites": "Lượt thích",
   "column.follow_requests": "Yêu cầu theo dõi",
   "column.home": "Bảng tin",
   "column.lists": "Danh sách",
-  "column.mutes": "Người dùng đã ẩn",
+  "column.mutes": "Người đã ẩn",
   "column.notifications": "Thông báo",
   "column.pins": "Tút ghim",
   "column.public": "Mạng liên hợp",
@@ -98,7 +98,7 @@
   "compose_form.poll.switch_to_single": "Chỉ cho phép chọn duy nhất một lựa chọn",
   "compose_form.publish": "Tút",
   "compose_form.publish_loud": "{publish}!",
-  "compose_form.sensitive.hide": "Nội dung nhạy cảm",
+  "compose_form.sensitive.hide": "{count, plural, one {Nội dung là nhạy cảm} other {Nội dung là nhạy cảm}}",
   "compose_form.sensitive.marked": "{count, plural, one {Nội dung đã đánh dấu là nhạy cảm} other {Nội dung đã đánh dấu là nhạy cảm}}",
   "compose_form.sensitive.unmarked": "{count, plural, one {Nội dung này bình thường} other {Nội dung này bình thường}}",
   "compose_form.spoiler.marked": "Hủy nội dung ẩn",
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "Kết quả tìm kiếm",
   "emoji_button.symbols": "Biểu tượng",
   "emoji_button.travel": "Du lịch",
+  "empty_column.account_suspended": "Tài khoản vô hiệu hóa",
   "empty_column.account_timeline": "Chưa có tút nào!",
   "empty_column.account_unavailable": "Tài khoản bị đình chỉ",
   "empty_column.blocks": "Bạn chưa chặn bất cứ ai.",
@@ -164,9 +165,9 @@
   "empty_column.home.public_timeline": "tút công khai",
   "empty_column.list": "Chưa có tút. Khi những người trong danh sách này đăng tút mới, chúng sẽ xuất hiện ở đây.",
   "empty_column.lists": "Bạn chưa tạo danh sách nào.",
-  "empty_column.mutes": "Bạn chưa ẩn người dùng nào.",
+  "empty_column.mutes": "Bạn chưa ẩn bất kỳ ai.",
   "empty_column.notifications": "Bạn chưa có thông báo nào. Hãy thử theo dõi hoặc nhắn tin cho một ai đó.",
-  "empty_column.public": "Trống trơn! Bạn hãy viết gì đó hoặc bắt đầu theo dõi người dùng khác",
+  "empty_column.public": "Trống trơn! Bạn hãy viết gì đó hoặc bắt đầu theo dõi những người khác",
   "error.unexpected_crash.explanation": "Trang này có thể không hiển thị chính xác do lỗi lập trình Mastodon hoặc vấn đề tương thích trình duyệt.",
   "error.unexpected_crash.explanation_addons": "Trang này không thể hiển thị do xung khắc với add-on của trình duyệt hoặc công cụ tự động dịch ngôn ngữ.",
   "error.unexpected_crash.next_steps": "Hãy thử làm mới trang. Nếu vẫn không được, bạn hãy vào Mastodon bằng một ứng dụng di động hoặc trình duyệt khác.",
@@ -178,7 +179,7 @@
   "follow_requests.unlocked_explanation": "Mặc dù tài khoản của bạn đang ở chế độ công khai, quản trị viên của {domain} vẫn tin rằng bạn sẽ muốn xem lại yêu cầu theo dõi từ những người khác.",
   "generic.saved": "Đã lưu",
   "getting_started.developers": "Nhà phát triển",
-  "getting_started.directory": "Kết bạn",
+  "getting_started.directory": "Mạng lưới",
   "getting_started.documentation": "Tài liệu",
   "getting_started.heading": "Quản lý",
   "getting_started.invite": "Mời bạn bè",
@@ -220,7 +221,7 @@
   "introduction.welcome.headline": "Hướng dẫn người mới",
   "introduction.welcome.text": "Chào mừng bạn đến mạng xã hội liên hợp! Tại đây bạn có thể đăng tải nội dung và trao đổi với bạn bè của mình trên các máy chủ khác nhau. Máy chủ {domain} là nơi lưu trữ trang cá nhân của bạn.",
   "keyboard_shortcuts.back": "trở lại",
-  "keyboard_shortcuts.blocked": "mở danh sách người dùng đã chặn",
+  "keyboard_shortcuts.blocked": "mở danh sách người đã chặn",
   "keyboard_shortcuts.boost": "chia sẻ",
   "keyboard_shortcuts.column": "mở các mục",
   "keyboard_shortcuts.compose": "mở khung soạn tút",
@@ -236,8 +237,8 @@
   "keyboard_shortcuts.hotkey": "Phím tắt",
   "keyboard_shortcuts.legend": "hiện bảng hướng dẫn này",
   "keyboard_shortcuts.local": "mở máy chủ của bạn",
-  "keyboard_shortcuts.mention": "nhắc đến người dùng",
-  "keyboard_shortcuts.muted": "mở danh sách người dùng đã ẩn",
+  "keyboard_shortcuts.mention": "nhắc đến ai đó",
+  "keyboard_shortcuts.muted": "mở danh sách người đã ẩn",
   "keyboard_shortcuts.my_profile": "mở trang cá nhân của bạn",
   "keyboard_shortcuts.notifications": "mở mục thông báo",
   "keyboard_shortcuts.open_media": "mở ảnh hoặc video",
@@ -254,11 +255,10 @@
   "keyboard_shortcuts.unfocus": "đưa con trỏ ra khỏi ô soạn thảo hoặc ô tìm kiếm",
   "keyboard_shortcuts.up": "di chuyển lên trên danh sách",
   "lightbox.close": "Đóng",
-  "lightbox.compress": "Thu gọn khung hình",
-  "lightbox.expand": "Mở rộng khung hình",
+  "lightbox.compress": "Thu nhỏ hình",
+  "lightbox.expand": "Phóng to hình",
   "lightbox.next": "Tiếp",
   "lightbox.previous": "Trước",
-  "lightbox.view_context": "Xem nội dung",
   "lists.account.add": "Thêm vào danh sách",
   "lists.account.remove": "Xóa khỏi danh sách",
   "lists.delete": "Xóa danh sách",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "Thay đổi tiêu đề",
   "lists.new.create": "Tạo danh sách mới",
   "lists.new.title_placeholder": "Tên danh sách mới",
-  "lists.replies_policy.all_replies": "Bất cứ người dùng nào đã theo dõi",
-  "lists.replies_policy.list_replies": "Thành viên trong danh sách",
-  "lists.replies_policy.no_replies": "Tắt bình luận",
+  "lists.replies_policy.followed": "Người theo dõi",
+  "lists.replies_policy.list": "Người trong danh sách",
+  "lists.replies_policy.none": "Không ai",
   "lists.replies_policy.title": "Cho phép bình luận với:",
   "lists.search": "Tìm kiếm những người mà bạn quan tâm",
   "lists.subheading": "Danh sách của bạn",
@@ -278,17 +278,17 @@
   "missing_indicator.label": "Không tìm thấy",
   "missing_indicator.sublabel": "Nội dung này không còn tồn tại",
   "mute_modal.duration": "Thời hạn",
-  "mute_modal.hide_notifications": "Ẩn thông báo từ người dùng này?",
+  "mute_modal.hide_notifications": "Ẩn thông báo từ người này?",
   "mute_modal.indefinite": "Vĩnh viễn",
   "navigation_bar.apps": "Apps",
-  "navigation_bar.blocks": "Người dùng đã chặn",
+  "navigation_bar.blocks": "Người đã chặn",
   "navigation_bar.bookmarks": "Đã lưu",
   "navigation_bar.community_timeline": "Cộng đồng",
   "navigation_bar.compose": "Viết tút mới",
   "navigation_bar.direct": "Tin nhắn",
   "navigation_bar.discover": "Khám phá",
   "navigation_bar.domain_blocks": "Máy chủ đã ẩn",
-  "navigation_bar.edit_profile": "Giới thiệu bản thân",
+  "navigation_bar.edit_profile": "Trang cá nhân",
   "navigation_bar.favourites": "Lượt thích",
   "navigation_bar.filters": "Bộ lọc từ ngữ",
   "navigation_bar.follow_requests": "Yêu cầu theo dõi",
@@ -297,7 +297,7 @@
   "navigation_bar.keyboard_shortcuts": "Phím tắt",
   "navigation_bar.lists": "Danh sách",
   "navigation_bar.logout": "Đăng xuất",
-  "navigation_bar.mutes": "Người dùng đã ẩn",
+  "navigation_bar.mutes": "Người đã ẩn",
   "navigation_bar.personal": "Cá nhân",
   "navigation_bar.pins": "Tút ghim",
   "navigation_bar.preferences": "Cài đặt",
@@ -307,8 +307,8 @@
   "notification.follow": "{name} theo dõi bạn",
   "notification.follow_request": "{name} yêu cầu theo dõi bạn",
   "notification.mention": "{name} nhắc đến bạn",
-  "notification.own_poll": "Cuộc bình chọn của bạn đã kết thúc",
-  "notification.poll": "Một cuộc bình chọn mà bạn tham gia đã kết thúc",
+  "notification.own_poll": "Cuộc bình chọn bạn tạo đã kết thúc",
+  "notification.poll": "Cuộc bình chọn của bạn đã kết thúc",
   "notification.reblog": "{name} chia sẻ tút của bạn",
   "notification.status": "{name} vừa đăng",
   "notifications.clear": "Xóa hết thông báo",
@@ -321,7 +321,7 @@
   "notifications.column_settings.follow": "Người theo dõi mới:",
   "notifications.column_settings.follow_request": "Yêu cầu theo dõi mới:",
   "notifications.column_settings.mention": "Lượt nhắc đến:",
-  "notifications.column_settings.poll": "Kết quả bình chọn:",
+  "notifications.column_settings.poll": "Kết quả:",
   "notifications.column_settings.push": "Thông báo đẩy",
   "notifications.column_settings.reblog": "Lượt chia sẻ mới:",
   "notifications.column_settings.show": "Thông báo trên thanh menu",
@@ -334,44 +334,46 @@
   "notifications.filter.mentions": "Lượt nhắc đến",
   "notifications.filter.polls": "Kết quả bình chọn",
   "notifications.filter.statuses": "Cập nhật từ những người bạn theo dõi",
+  "notifications.grant_permission": "Cho phép.",
   "notifications.group": "{count} thông báo",
   "notifications.mark_as_read": "Đánh dấu tất cả thông báo là đã đọc",
   "notifications.permission_denied": "Trình duyệt không cho phép hiển thị thông báo trên màn hình.",
   "notifications.permission_denied_alert": "Không thể bật thông báo trên màn hình bởi vì trình duyệt đã cấm trước đó",
+  "notifications.permission_required": "Không hiện thông báo trên màn hình bởi vì chưa cho phép.",
   "notifications_permission_banner.enable": "Cho phép thông báo trên màn hình",
   "notifications_permission_banner.how_to_control": "Hãy bật thông báo trên màn hình để không bỏ lỡ những thông báo từ Mastodon. Một khi đã bật, bạn có thể lựa chọn từng loại thông báo khác nhau thông qua {icon} nút bên dưới.",
   "notifications_permission_banner.title": "Không bỏ lỡ điều thú vị nào",
   "picture_in_picture.restore": "Hiển thị bình thường",
   "poll.closed": "Kết thúc",
   "poll.refresh": "Làm mới",
-  "poll.total_people": "{count, plural, one {# người bình chọn} other {# người bình chọn}}",
-  "poll.total_votes": "{count, plural, one {# bình chọn} other {# bình chọn}}",
-  "poll.vote": "Khảo sát",
-  "poll.voted": "Bạn đã bình chọn câu trả lời này",
-  "poll_button.add_poll": "Tạo cuộc bình chọn",
+  "poll.total_people": "{count, plural, one {# người} other {# người}}",
+  "poll.total_votes": "{count, plural, one {# người} other {# người}}",
+  "poll.vote": "Bình chọn",
+  "poll.voted": "Bạn đã bình chọn rồi",
+  "poll_button.add_poll": "Tạo bình chọn",
   "poll_button.remove_poll": "Hủy cuộc bình chọn",
   "privacy.change": "Thay đổi quyền riêng tư",
   "privacy.direct.long": "Chỉ người được nhắc đến mới thấy",
   "privacy.direct.short": "Tin nhắn",
   "privacy.private.long": "Chỉ dành cho người theo dõi",
-  "privacy.private.short": "Chỉ người theo dõi",
+  "privacy.private.short": "Người theo dõi",
   "privacy.public.long": "Hiện trên bảng tin máy chủ",
   "privacy.public.short": "Công khai",
-  "privacy.unlisted.long": "Công khai nhưng không hiện trên bảng tin máy chủ",
-  "privacy.unlisted.short": "Mở",
+  "privacy.unlisted.long": "Không hiện trên bảng tin máy chủ",
+  "privacy.unlisted.short": "Riêng tư",
   "refresh": "Làm mới",
   "regeneration_indicator.label": "Đang tải…",
   "regeneration_indicator.sublabel": "Bảng tin của bạn đang được cập nhật!",
   "relative_time.days": "{number} ngày",
   "relative_time.hours": "{number}h",
-  "relative_time.just_now": "vừa xong",
+  "relative_time.just_now": "mới",
   "relative_time.minutes": "{number}m",
   "relative_time.seconds": "{number}s",
   "relative_time.today": "hôm nay",
   "reply_indicator.cancel": "Hủy bỏ",
   "report.forward": "Chuyển đến {target}",
-  "report.forward_hint": "Người dùng này ở máy chủ khác. Gửi một báo cáo ẩn danh tới máy chủ đó?",
-  "report.hint": "Hãy cho quản trị viên biết lý do vì sao bạn báo cáo người dùng này:",
+  "report.forward_hint": "Người này thuộc máy chủ khác. Gửi một báo cáo ẩn danh tới máy chủ đó?",
+  "report.hint": "Hãy cho quản trị viên biết lý do vì sao bạn báo cáo người này:",
   "report.placeholder": "Bổ sung thêm",
   "report.submit": "Gửi đi",
   "report.target": "Báo cáo {target}",
@@ -380,12 +382,12 @@
   "search_popout.tips.full_text": "Nội dung trả về bao gồm những tút mà bạn đã viết, thích, chia sẻ hoặc những tút có nhắc đến bạn. Bạn cũng có thể tìm địa chỉ người dùng, tên hiển thị và hashtag.",
   "search_popout.tips.hashtag": "hashtag",
   "search_popout.tips.status": "tút",
-  "search_popout.tips.text": "Nội dung trả về là địa chỉ người dùng, tên hiển thị và hashtag",
+  "search_popout.tips.text": "Nội dung trả về là tên người dùng, tên hiển thị và hashtag",
   "search_popout.tips.user": "người dùng",
   "search_results.accounts": "Người dùng",
   "search_results.hashtags": "Hashtags",
   "search_results.statuses": "Tút",
-  "search_results.statuses_fts_disabled": "Máy chủ của bạn không bật chức năng tìm kiếm tút.",
+  "search_results.statuses_fts_disabled": "Máy chủ của bạn không bật tính năng tìm kiếm tút.",
   "search_results.total": "{count, number} {count, plural, one {kết quả} other {kết quả}}",
   "status.admin_account": "Mở giao diện quản trị @{name}",
   "status.admin_status": "Mở tút này trong giao diện quản trị",
@@ -409,7 +411,7 @@
   "status.open": "Xem nguyên văn",
   "status.pin": "Ghim lên trang cá nhân",
   "status.pinned": "Tút đã ghim",
-  "status.read_more": "Đọc thêm tút",
+  "status.read_more": "Đọc tiếp",
   "status.reblog": "Chia sẻ",
   "status.reblog_private": "Chia sẻ với người có thể xem",
   "status.reblogged_by": "{name} chia sẻ",
@@ -425,7 +427,7 @@
   "status.show_less_all": "Thu gọn toàn bộ",
   "status.show_more": "Mở rộng",
   "status.show_more_all": "Hiển thị tất cả",
-  "status.show_thread": "Xem thêm",
+  "status.show_thread": "Liên quan",
   "status.uncached_media_warning": "Giới hạn",
   "status.unmute_conversation": "Quan tâm",
   "status.unpin": "Bỏ ghim trên trang cá nhân",
@@ -454,7 +456,7 @@
   "upload_area.title": "Kéo và thả để tải lên",
   "upload_button.label": "Thêm media (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_error.limit": "Tập tin tải lên vượt quá giới hạn cho phép.",
-  "upload_error.poll": "Cuộc bình chọn không cho phép đính kèm tập tin.",
+  "upload_error.poll": "Không cho phép đính kèm tập tin.",
   "upload_form.audio_description": "Mô tả cho người mất thính giác",
   "upload_form.description": "Mô tả cho người khiếm thị",
   "upload_form.edit": "Biên tập",
diff --git a/app/javascript/mastodon/locales/whitelist_tt.json b/app/javascript/mastodon/locales/whitelist_tt.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_tt.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/mastodon/locales/zgh.json b/app/javascript/mastodon/locales/zgh.json
index e79399ea1..e2c69c116 100644
--- a/app/javascript/mastodon/locales/zgh.json
+++ b/app/javascript/mastodon/locales/zgh.json
@@ -4,23 +4,23 @@
   "account.badges.bot": "ⴰⴱⵓⵜ",
   "account.badges.group": "ⵜⴰⵔⴰⴱⴱⵓⵜ",
   "account.block": "ⴳⴷⵍ @{name}",
-  "account.block_domain": "Block domain {domain}",
+  "account.block_domain": "ⴳⴷⵍ ⵉⴳⵔ {domain}",
   "account.blocked": "ⵉⵜⵜⵓⴳⴷⵍ",
-  "account.browse_more_on_origin_server": "Browse more on the original profile",
+  "account.browse_more_on_origin_server": "ⵙⵜⴰⵔⴰ ⵓⴳⴳⴰⵔ ⴳ ⵉⴼⵔⵙ ⴰⵏⵚⵍⵉ",
   "account.cancel_follow_request": "ⵙⵔ ⵜⵓⵜⵔⴰ ⵏ ⵓⴹⴼⵕ",
-  "account.direct": "Direct message @{name}",
+  "account.direct": "ⵜⵓⵣⵉⵏⵜ ⵜⵓⵙⵔⵉⴷⵜ @{name}",
   "account.disable_notifications": "Stop notifying me when @{name} posts",
-  "account.domain_blocked": "Domain blocked",
+  "account.domain_blocked": "ⵉⵜⵜⵓⴳⴷⵍ ⵉⴳⵔ",
   "account.edit_profile": "ⵙⵏⴼⵍ ⵉⴼⵔⵙ",
   "account.enable_notifications": "Notify me when @{name} posts",
   "account.endorse": "Feature on profile",
   "account.follow": "ⴹⴼⵕ",
-  "account.followers": "Followers",
+  "account.followers": "ⵉⵎⴹⴼⴰⵕⵏ",
   "account.followers.empty": "No one follows this user yet.",
   "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
   "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
   "account.follows.empty": "This user doesn't follow anyone yet.",
-  "account.follows_you": "Follows you",
+  "account.follows_you": "ⴹⴼⵕⵏ ⴽⵯⵏ",
   "account.hide_reblogs": "Hide boosts from @{name}",
   "account.last_status": "Last active",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
@@ -31,18 +31,18 @@
   "account.mute": "Mute @{name}",
   "account.mute_notifications": "Mute notifications from @{name}",
   "account.muted": "Muted",
-  "account.never_active": "Never",
+  "account.never_active": "ⵓⵙⴰⵔ",
   "account.posts": "Toots",
   "account.posts_with_replies": "Toots and replies",
   "account.report": "Report @{name}",
   "account.requested": "Awaiting approval",
-  "account.share": "Share @{name}'s profile",
+  "account.share": "ⴱⴹⵓ ⵉⴼⵔⵙ ⵏ @{name}",
   "account.show_reblogs": "Show boosts from @{name}",
   "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}",
   "account.unblock": "Unblock @{name}",
   "account.unblock_domain": "Unblock domain {domain}",
   "account.unendorse": "Don't feature on profile",
-  "account.unfollow": "Unfollow",
+  "account.unfollow": "ⴽⴽⵙ ⴰⴹⴼⴼⵓⵕ",
   "account.unmute": "Unmute @{name}",
   "account.unmute_notifications": "Unmute notifications from @{name}",
   "account_note.placeholder": "Click to add a note",
@@ -51,21 +51,21 @@
   "alert.unexpected.message": "An unexpected error occurred.",
   "alert.unexpected.title": "Oops!",
   "announcement.announcement": "Announcement",
-  "autosuggest_hashtag.per_week": "{count} per week",
+  "autosuggest_hashtag.per_week": "{count} ⵙ ⵉⵎⴰⵍⴰⵙⵙ",
   "boost_modal.combo": "You can press {combo} to skip this next time",
   "bundle_column_error.body": "Something went wrong while loading this component.",
-  "bundle_column_error.retry": "Try again",
-  "bundle_column_error.title": "Network error",
+  "bundle_column_error.retry": "ⴰⵍⵙ ⴰⵔⵎ",
+  "bundle_column_error.title": "ⴰⵣⴳⴰⵍ ⵏ ⵓⵥⵟⵟⴰ",
   "bundle_modal_error.close": "ⵔⴳⵍ",
   "bundle_modal_error.message": "Something went wrong while loading this component.",
-  "bundle_modal_error.retry": "Try again",
-  "column.blocks": "Blocked users",
+  "bundle_modal_error.retry": "ⴰⵍⵙ ⴰⵔⵎ",
+  "column.blocks": "ⵉⵏⵙⵙⵎⵔⵙⵏ ⵜⵜⵓⴳⴷⵍⵏⵉⵏ",
   "column.bookmarks": "Bookmarks",
   "column.community": "Local timeline",
   "column.direct": "Direct messages",
   "column.directory": "Browse profiles",
   "column.domain_blocks": "Blocked domains",
-  "column.favourites": "Favourites",
+  "column.favourites": "ⵜⵓⴼⵓⵜⵉⵏ",
   "column.follow_requests": "Follow requests",
   "column.home": "ⴰⵙⵏⵓⴱⴳ",
   "column.lists": "ⵜⵉⵍⴳⴰⵎⵉⵏ",
@@ -73,26 +73,26 @@
   "column.notifications": "ⵜⵉⵏⵖⵎⵉⵙⵉⵏ",
   "column.pins": "Pinned toot",
   "column.public": "Federated timeline",
-  "column_back_button.label": "Back",
-  "column_header.hide_settings": "Hide settings",
+  "column_back_button.label": "ⴰⵖⵓⵍ",
+  "column_header.hide_settings": "ⵙⵏⵜⵍ ⵜⵉⵙⵖⴰⵍ",
   "column_header.moveLeft_settings": "Move column to the left",
   "column_header.moveRight_settings": "Move column to the right",
-  "column_header.pin": "Pin",
-  "column_header.show_settings": "Show settings",
-  "column_header.unpin": "Unpin",
+  "column_header.pin": "ⵖⵏⵙ",
+  "column_header.show_settings": "ⵙⵎⴰⵍ ⵜⵉⵙⵖⴰⵍ",
+  "column_header.unpin": "ⴽⴽⵙ ⴰⵖⵏⴰⵙ",
   "column_subheading.settings": "ⵜⵉⵙⵖⴰⵍ",
-  "community.column_settings.local_only": "Local only",
-  "community.column_settings.media_only": "Media only",
+  "community.column_settings.local_only": "ⵖⴰⵙ ⴰⴷⵖⴰⵔⴰⵏ",
+  "community.column_settings.media_only": "ⵖⴰⵙ ⵉⵙⵏⵖⵎⵉⵙⵏ",
   "community.column_settings.remote_only": "Remote only",
   "compose_form.direct_message_warning": "This toot will only be sent to all the mentioned users.",
-  "compose_form.direct_message_warning_learn_more": "Learn more",
+  "compose_form.direct_message_warning_learn_more": "ⵙⵙⵏ ⵓⴳⴳⴰⵔ",
   "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
   "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
   "compose_form.lock_disclaimer.lock": "locked",
-  "compose_form.placeholder": "What is on your mind?",
+  "compose_form.placeholder": "ⵎⴰⵢⴷ ⵉⵍⵍⴰⵏ ⴳ ⵉⵅⴼ ⵏⵏⴽ?",
   "compose_form.poll.add_option": "Add a choice",
   "compose_form.poll.duration": "Poll duration",
-  "compose_form.poll.option_placeholder": "Choice {number}",
+  "compose_form.poll.option_placeholder": "ⴰⵙⵜⵜⴰⵢ {number}",
   "compose_form.poll.remove_option": "Remove this choice",
   "compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices",
   "compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
@@ -106,29 +106,29 @@
   "compose_form.spoiler_placeholder": "Write your warning here",
   "confirmation_modal.cancel": "ⵙⵔ",
   "confirmations.block.block_and_report": "Block & Report",
-  "confirmations.block.confirm": "Block",
-  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.block.confirm": "ⴳⴷⵍ",
+  "confirmations.block.message": "ⵉⵙ ⵏⵉⵜ ⵜⵅⵙⴷ ⴰⴷ ⵜⴳⴷⵍⴷ {name}?",
   "confirmations.delete.confirm": "ⴽⴽⵙ",
-  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.delete.message": "ⵉⵙ ⵏⵉⵜ ⵜⵅⵙⴷ ⴰⴷ ⵜⴽⴽⵙⴷ ⵜⴰⵥⵕⵉⴳⵜ ⴰ?",
   "confirmations.delete_list.confirm": "ⴽⴽⵙ",
   "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
   "confirmations.domain_block.confirm": "Hide entire domain",
   "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.",
-  "confirmations.logout.confirm": "Log out",
-  "confirmations.logout.message": "Are you sure you want to log out?",
-  "confirmations.mute.confirm": "Mute",
+  "confirmations.logout.confirm": "ⴼⴼⵖ",
+  "confirmations.logout.message": "ⵉⵙ ⵏⵉⵜ ⵜⵅⵙⴷ ⴰⴷ ⵜⴼⴼⵖⴷ?",
+  "confirmations.mute.confirm": "ⵥⵥⵉⵥⵏ",
   "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.",
   "confirmations.mute.message": "Are you sure you want to mute {name}?",
   "confirmations.redraft.confirm": "Delete & redraft",
   "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.",
   "confirmations.reply.confirm": "ⵔⴰⵔ",
   "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
-  "confirmations.unfollow.confirm": "Unfollow",
-  "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
-  "conversation.delete": "Delete conversation",
+  "confirmations.unfollow.confirm": "ⴽⴽⵙ ⴰⴹⴼⴼⵓⵕ",
+  "confirmations.unfollow.message": "ⵉⵙ ⵏⵉⵜ ⵜⵅⵙⴷ ⴰⴷ ⵜⴽⴽⵙⴷ ⴰⴹⴼⴼⵓⵕ ⵉ {name}?",
+  "conversation.delete": "ⴽⴽⵙ ⴰⵎⵙⴰⵡⴰⵍ",
   "conversation.mark_as_read": "Mark as read",
   "conversation.open": "View conversation",
-  "conversation.with": "With {names}",
+  "conversation.with": "ⴰⴽⴷ {names}",
   "directory.federated": "From known fediverse",
   "directory.local": "From {domain} only",
   "directory.new_arrivals": "New arrivals",
@@ -141,14 +141,15 @@
   "emoji_button.food": "Food & Drink",
   "emoji_button.label": "Insert emoji",
   "emoji_button.nature": "Nature",
-  "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
+  "emoji_button.not_found": "ⵓⵍⴰ ⵉⵎⵓⵊⵉ!! (╯°□°)╯︵ ┻━┻",
   "emoji_button.objects": "Objects",
-  "emoji_button.people": "People",
+  "emoji_button.people": "ⵎⵉⴷⴷⵏ",
   "emoji_button.recent": "Frequently used",
-  "emoji_button.search": "Search...",
+  "emoji_button.search": "ⵔⵣⵓ...",
   "emoji_button.search_results": "Search results",
-  "emoji_button.symbols": "Symbols",
+  "emoji_button.symbols": "ⵜⵉⵎⴰⵜⴰⵔⵉⵏ",
   "emoji_button.travel": "Travel & Places",
+  "empty_column.account_suspended": "Account suspended",
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
@@ -185,11 +186,11 @@
   "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}.",
   "getting_started.security": "ⵜⵉⵙⵖⴰⵍ ⵏ ⵓⵎⵉⴹⴰⵏ",
   "getting_started.terms": "Terms of service",
-  "hashtag.column_header.tag_mode.all": "and {additional}",
-  "hashtag.column_header.tag_mode.any": "or {additional}",
+  "hashtag.column_header.tag_mode.all": "ⴷ {additional}",
+  "hashtag.column_header.tag_mode.any": "ⵏⵖ {additional}",
   "hashtag.column_header.tag_mode.none": "without {additional}",
   "hashtag.column_settings.select.no_options_message": "No suggestions found",
-  "hashtag.column_settings.select.placeholder": "Enter hashtags…",
+  "hashtag.column_settings.select.placeholder": "ⵙⴽⵛⵎ ⴰⵀⴰⵛⵟⴰⴳ…",
   "hashtag.column_settings.tag_mode.all": "All of these",
   "hashtag.column_settings.tag_mode.any": "Any of these",
   "hashtag.column_settings.tag_mode.none": "None of these",
@@ -199,15 +200,15 @@
   "home.column_settings.show_replies": "Show replies",
   "home.hide_announcements": "Hide announcements",
   "home.show_announcements": "Show announcements",
-  "intervals.full.days": "{number, plural, one {# day} other {# days}}",
+  "intervals.full.days": "{number, plural, one {# ⵡⴰⵙⵙ} other {# ⵡⵓⵙⵙⴰⵏ}}",
   "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
-  "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}",
+  "intervals.full.minutes": "{number, plural, one {# ⵜⵓⵙⴷⵉⴷⵜ} other {# ⵜⵓⵙⴷⵉⴷⵉⵏ}}",
   "introduction.federation.action": "Next",
   "introduction.federation.federated.headline": "Federated",
   "introduction.federation.federated.text": "Public posts from other servers of the fediverse will appear in the federated timeline.",
   "introduction.federation.home.headline": "ⴰⵙⵏⵓⴱⴳ",
   "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!",
-  "introduction.federation.local.headline": "Local",
+  "introduction.federation.local.headline": "ⴰⴷⵖⴰⵔⴰⵏ",
   "introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
   "introduction.interactions.action": "Finish toot-orial!",
   "introduction.interactions.favourite.headline": "Favourite",
@@ -258,23 +259,22 @@
   "lightbox.expand": "Expand image view box",
   "lightbox.next": "Next",
   "lightbox.previous": "Previous",
-  "lightbox.view_context": "View context",
   "lists.account.add": "ⵔⵏⵓ ⵖⵔ ⵜⵍⴳⴰⵎⵜ",
   "lists.account.remove": "ⴽⴽⵙ ⵙⴳ ⵜⵍⴳⴰⵎⵜ",
   "lists.delete": "ⴽⴽⵙ ⵜⴰⵍⴳⴰⵎⵜ",
-  "lists.edit": "Edit list",
-  "lists.edit.submit": "Change title",
-  "lists.new.create": "Add list",
-  "lists.new.title_placeholder": "New list title",
-  "lists.replies_policy.all_replies": "Any followed user",
-  "lists.replies_policy.list_replies": "Members of the list",
-  "lists.replies_policy.no_replies": "No one",
+  "lists.edit": "ⵙⵏⴼⵍ ⵜⴰⵍⴳⴰⵎⵜ",
+  "lists.edit.submit": "ⵙⵏⴼⵍ ⴰⵣⵡⵍ",
+  "lists.new.create": "ⵙⴽⵔ ⵜⴰⵍⴳⴰⵎⵜ",
+  "lists.new.title_placeholder": "ⴰⵣⵡⵍ ⵏ ⵜⵍⴳⴰⵎⵜ ⵜⴰⵎⴰⵢⵏⵓⵜ",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
   "lists.replies_policy.title": "Show replies to:",
   "lists.search": "Search among people you follow",
-  "lists.subheading": "Your lists",
-  "load_pending": "{count, plural, one {# new item} other {# new items}}",
-  "loading_indicator.label": "Loading...",
-  "media_gallery.toggle_visible": "Hide {number, plural, one {image} other {images}}",
+  "lists.subheading": "ⵜⵉⵍⴳⴰⵎⵉⵏ ⵏⵏⴽ",
+  "load_pending": "{count, plural, one {# ⵓⴼⵔⴷⵉⵙ ⴰⵎⴰⵢⵏⵓ} other {# ⵉⴼⵔⴷⴰⵙ ⵉⵎⴰⵢⵏⵓⵜⵏ}}",
+  "loading_indicator.label": "ⴰⵣⴷⴰⵎ...",
+  "media_gallery.toggle_visible": "ⴼⴼⵔ {number, plural, one {ⵜⴰⵡⵍⴰⴼⵜ} other {ⵜⵉⵡⵍⴰⴼⵉⵏ}}",
   "missing_indicator.label": "Not found",
   "missing_indicator.sublabel": "This resource could not be found",
   "mute_modal.duration": "Duration",
@@ -296,7 +296,7 @@
   "navigation_bar.info": "About this server",
   "navigation_bar.keyboard_shortcuts": "Hotkeys",
   "navigation_bar.lists": "Lists",
-  "navigation_bar.logout": "Logout",
+  "navigation_bar.logout": "ⴼⴼⵖ",
   "navigation_bar.mutes": "Muted users",
   "navigation_bar.personal": "Personal",
   "navigation_bar.pins": "Pinned toots",
@@ -325,26 +325,28 @@
   "notifications.column_settings.push": "Push notifications",
   "notifications.column_settings.reblog": "Boosts:",
   "notifications.column_settings.show": "Show in column",
-  "notifications.column_settings.sound": "Play sound",
+  "notifications.column_settings.sound": "ⵖⵔ ⵉⵎⵙⵍⵉ",
   "notifications.column_settings.status": "New toots:",
-  "notifications.filter.all": "All",
+  "notifications.filter.all": "ⴰⴽⴽⵯ",
   "notifications.filter.boosts": "Boosts",
   "notifications.filter.favourites": "Favourites",
   "notifications.filter.follows": "Follows",
   "notifications.filter.mentions": "Mentions",
   "notifications.filter.polls": "Poll results",
   "notifications.filter.statuses": "Updates from people you follow",
-  "notifications.group": "{count} notifications",
+  "notifications.grant_permission": "Grant permission.",
+  "notifications.group": "{count} ⵜⵏⵖⵎⵉⵙⵉⵏ",
   "notifications.mark_as_read": "Mark every notification as read",
   "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
   "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Enable desktop notifications",
   "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.title": "Never miss a thing",
   "picture_in_picture.restore": "Put it back",
-  "poll.closed": "Closed",
+  "poll.closed": "ⵉⵜⵜⵓⵔⴳⵍ",
   "poll.refresh": "Refresh",
-  "poll.total_people": "{count, plural, one {# person} other {# people}}",
+  "poll.total_people": "{count, plural, one {# ⵓⴼⴳⴰⵏ} other {# ⵉⴼⴳⴰⵏⵏ}}",
   "poll.total_votes": "{count, plural, one {# vote} other {# votes}}",
   "poll.vote": "Vote",
   "poll.voted": "You voted for this answer",
@@ -352,24 +354,24 @@
   "poll_button.remove_poll": "Remove poll",
   "privacy.change": "Adjust status privacy",
   "privacy.direct.long": "Visible for mentioned users only",
-  "privacy.direct.short": "Direct",
+  "privacy.direct.short": "ⵜⵓⵔⴷⵉⵜ",
   "privacy.private.long": "Visible for followers only",
   "privacy.private.short": "Followers-only",
   "privacy.public.long": "Visible for all, shown in public timelines",
-  "privacy.public.short": "Public",
+  "privacy.public.short": "ⵜⴰⴳⴷⵓⴷⴰⵏⵜ",
   "privacy.unlisted.long": "Visible for all, but not in public timelines",
   "privacy.unlisted.short": "Unlisted",
   "refresh": "Refresh",
   "regeneration_indicator.label": "ⴰⵣⴷⴰⵎ…",
   "regeneration_indicator.sublabel": "Your home feed is being prepared!",
-  "relative_time.days": "{number}d",
-  "relative_time.hours": "{number}h",
+  "relative_time.days": "{number}ⴰⵙ",
+  "relative_time.hours": "{number}ⵙⵔⴳ",
   "relative_time.just_now": "ⴷⵖⵉ",
-  "relative_time.minutes": "{number}m",
-  "relative_time.seconds": "{number}s",
-  "relative_time.today": "today",
-  "reply_indicator.cancel": "Cancel",
-  "report.forward": "Forward to {target}",
+  "relative_time.minutes": "{number}ⵙⴷ",
+  "relative_time.seconds": "{number}ⵙⵏ",
+  "relative_time.today": "ⴰⵙⵙⴰ",
+  "reply_indicator.cancel": "ⵙⵔ",
+  "report.forward": "ⵙⵙⵉⴼⴹ ⵉ {target}",
   "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
   "report.hint": "The report will be sent to your server moderators. You can provide an explanation of why you are reporting this account below:",
   "report.placeholder": "Additional comments",
@@ -382,30 +384,30 @@
   "search_popout.tips.status": "status",
   "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
   "search_popout.tips.user": "user",
-  "search_results.accounts": "People",
-  "search_results.hashtags": "Hashtags",
+  "search_results.accounts": "ⵎⵉⴷⴷⵏ",
+  "search_results.hashtags": "ⵀⴰⵛⵟⴰⴳ",
   "search_results.statuses": "Toots",
   "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.",
   "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
-  "status.block": "Block @{name}",
+  "status.block": "ⴳⴷⵍ @{name}",
   "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.copy": "Copy link to status",
   "status.delete": "ⴽⴽⵙ",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "ⵜⵓⵣⵉⵏⵜ ⵜⵓⵙⵔⵉⴷⵜ ⵉ @{name}",
   "status.embed": "Embed",
   "status.favourite": "Favourite",
   "status.filtered": "Filtered",
-  "status.load_more": "Load more",
+  "status.load_more": "ⵙⵙⵉⵍⵉ ⵓⴳⴳⴰⵔ",
   "status.media_hidden": "Media hidden",
   "status.mention": "Mention @{name}",
-  "status.more": "More",
-  "status.mute": "Mute @{name}",
-  "status.mute_conversation": "Mute conversation",
+  "status.more": "ⵓⴳⴳⴰⵔ",
+  "status.mute": "ⵥⵥⵉⵥⵏ @{name}",
+  "status.mute_conversation": "ⵥⵥⵉⵥⵏ ⴰⵎⵙⴰⵡⴰⵍ",
   "status.open": "Expand this status",
   "status.pin": "Pin on profile",
   "status.pinned": "Pinned toot",
@@ -420,11 +422,11 @@
   "status.replyAll": "Reply to thread",
   "status.report": "Report @{name}",
   "status.sensitive_warning": "Sensitive content",
-  "status.share": "Share",
-  "status.show_less": "Show less",
-  "status.show_less_all": "Show less for all",
-  "status.show_more": "Show more",
-  "status.show_more_all": "Show more for all",
+  "status.share": "ⴱⴹⵓ",
+  "status.show_less": "ⵙⵎⴰⵍ ⴷⵔⵓⵙ",
+  "status.show_less_all": "ⵙⵎⴰⵍ ⴷⵔⵓⵙ ⵉ ⵎⴰⵕⵕⴰ",
+  "status.show_more": "ⵙⵎⴰⵍ ⵓⴳⴳⴰⵔ",
+  "status.show_more_all": "ⵙⵎⴰⵍ ⵓⴳⴳⴰⵔ ⵉ ⵎⴰⵕⵕⴰ",
   "status.show_thread": "Show thread",
   "status.uncached_media_warning": "Not available",
   "status.unmute_conversation": "Unmute conversation",
@@ -433,16 +435,16 @@
   "suggestions.header": "You might be interested in…",
   "tabs_bar.federated_timeline": "Federated",
   "tabs_bar.home": "ⴰⵙⵏⵓⴱⴳ",
-  "tabs_bar.local_timeline": "Local",
+  "tabs_bar.local_timeline": "ⴰⴷⵖⴰⵔⴰⵏ",
   "tabs_bar.notifications": "ⵜⵉⵏⵖⵎⵉⵙⵉⵏ",
   "tabs_bar.search": "ⵔⵣⵓ",
-  "time_remaining.days": "{number, plural, one {# day} other {# days}} left",
+  "time_remaining.days": "{number, plural, one {# ⵡⴰⵙⵙ} other {# ⵡⵓⵙⵙⴰⵏ}} ⵉⵇⵇⵉⵎⵏ",
   "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left",
   "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left",
   "time_remaining.moments": "Moments remaining",
   "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left",
   "timeline_hint.remote_resource_not_displayed": "{resource} from other servers are not displayed.",
-  "timeline_hint.resources.followers": "Followers",
+  "timeline_hint.resources.followers": "ⵉⵎⴹⴼⴰⵕⵏ",
   "timeline_hint.resources.follows": "Follows",
   "timeline_hint.resources.statuses": "Older toots",
   "trends.counter_by_accounts": "{count, plural, one {{counter} person} other {{counter} people}} talking",
@@ -479,6 +481,6 @@
   "video.hide": "Hide video",
   "video.mute": "Mute sound",
   "video.pause": "Pause",
-  "video.play": "Play",
+  "video.play": "ⵖⵔ",
   "video.unmute": "Unmute sound"
 }
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index 956067e6f..6c746bf96 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -133,7 +133,7 @@
   "directory.local": "仅来自 {domain}",
   "directory.new_arrivals": "新来者",
   "directory.recently_active": "最近活跃",
-  "embed.instructions": "要在你的网站上嵌入这条嘟文,请复制以下代码。",
+  "embed.instructions": "要在你的网站上嵌入此嘟文,请复制以下代码。",
   "embed.preview": "它会像这样显示出来:",
   "emoji_button.activity": "活动",
   "emoji_button.custom": "自定义",
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "搜索结果",
   "emoji_button.symbols": "符号",
   "emoji_button.travel": "旅行和地点",
+  "empty_column.account_suspended": "账户被封禁",
   "empty_column.account_timeline": "这里没有嘟文!",
   "empty_column.account_unavailable": "个人资料不可用",
   "empty_column.blocks": "你目前没有屏蔽任何用户。",
@@ -162,7 +163,7 @@
   "empty_column.hashtag": "这个话题标签下暂时没有内容。",
   "empty_column.home": "你还没有关注任何用户。快看看{public},向其他人问个好吧。",
   "empty_column.home.public_timeline": "公共时间轴",
-  "empty_column.list": "这个列表中暂时没有内容。列表中用户所发送的的新嘟文将会在这里显示。",
+  "empty_column.list": "此列表中暂时没有内容。列表中用户所发送的的新嘟文将会在这里显示。",
   "empty_column.lists": "你还没有创建过列表。你创建的列表会在这里显示。",
   "empty_column.mutes": "你没有隐藏任何用户。",
   "empty_column.notifications": "你还没有收到过任何通知,快和其他用户互动吧。",
@@ -258,7 +259,6 @@
   "lightbox.expand": "放大查看图片",
   "lightbox.next": "下一个",
   "lightbox.previous": "上一个",
-  "lightbox.view_context": "查看上下文",
   "lists.account.add": "添加到列表",
   "lists.account.remove": "从列表中移除",
   "lists.delete": "删除列表",
@@ -266,9 +266,9 @@
   "lists.edit.submit": "更改标题",
   "lists.new.create": "新建列表",
   "lists.new.title_placeholder": "新列表的标题",
-  "lists.replies_policy.all_replies": "任何关注的用户",
-  "lists.replies_policy.list_replies": "列表成员",
-  "lists.replies_policy.no_replies": "没有人",
+  "lists.replies_policy.followed": "任何被关注的用户",
+  "lists.replies_policy.list": "列表成员",
+  "lists.replies_policy.none": "没有人",
   "lists.replies_policy.title": "显示回复给:",
   "lists.search": "搜索你关注的人",
   "lists.subheading": "你的列表",
@@ -334,10 +334,12 @@
   "notifications.filter.mentions": "提及",
   "notifications.filter.polls": "投票结果",
   "notifications.filter.statuses": "你关注的人的动态",
+  "notifications.grant_permission": "授予权限",
   "notifications.group": "{count} 条通知",
   "notifications.mark_as_read": "将所有通知标为已读",
   "notifications.permission_denied": "由于权限被拒绝,无法启用桌面通知。",
   "notifications.permission_denied_alert": "由于在此之前浏览器权限请求就已被拒绝,所以启用桌面通知失败",
+  "notifications.permission_required": "所需权限未被授予,所以桌面通知不可用",
   "notifications_permission_banner.enable": "启用桌面通知",
   "notifications_permission_banner.how_to_control": "启用桌面通知以在 Mastodon 未打开时接收通知。您可以通过交互通过上面的 {icon} 按钮来精细控制可以发送桌面通知的交互类型。",
   "notifications_permission_banner.title": "精彩不容错过",
@@ -401,7 +403,7 @@
   "status.favourite": "喜欢",
   "status.filtered": "已过滤",
   "status.load_more": "加载更多",
-  "status.media_hidden": "隐藏媒体内容",
+  "status.media_hidden": "已隐藏的媒体内容",
   "status.mention": "提及 @{name}",
   "status.more": "更多",
   "status.mute": "隐藏 @{name}",
@@ -427,7 +429,7 @@
   "status.show_more_all": "显示所有内容",
   "status.show_thread": "显示全部对话",
   "status.uncached_media_warning": "暂不可用",
-  "status.unmute_conversation": "不再隐藏此对话",
+  "status.unmute_conversation": "将此对话解除静音",
   "status.unpin": "在个人资料页面取消置顶",
   "suggestions.dismiss": "关闭建议",
   "suggestions.header": "您可能会感兴趣…",
@@ -452,7 +454,7 @@
   "units.short.million": "{count}M",
   "units.short.thousand": "{count}K",
   "upload_area.title": "将文件拖放到此处开始上传",
-  "upload_button.label": "上传媒体文件",
+  "upload_button.label": "上传图片、视频或音频",
   "upload_error.limit": "文件大小超过限制。",
   "upload_error.poll": "投票中不允许上传文件。",
   "upload_form.audio_description": "为听障人士添加文字描述",
diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json
index 832e2a6a0..1a62ccbb3 100644
--- a/app/javascript/mastodon/locales/zh-HK.json
+++ b/app/javascript/mastodon/locales/zh-HK.json
@@ -4,27 +4,27 @@
   "account.badges.bot": "機械人",
   "account.badges.group": "群組",
   "account.block": "封鎖 @{name}",
-  "account.block_domain": "隱藏來自 {domain} 的一切文章",
-  "account.blocked": "封鎖",
-  "account.browse_more_on_origin_server": "在該服務器的個人檔案頁上瀏覽更多",
+  "account.block_domain": "封鎖來自 {domain} 的一切文章",
+  "account.blocked": "已封鎖",
+  "account.browse_more_on_origin_server": "瀏覽原服務站上的個人資料頁",
   "account.cancel_follow_request": "取消關注請求",
   "account.direct": "私訊 @{name}",
   "account.disable_notifications": "如果 @{name} 發文請不要再通知我",
-  "account.domain_blocked": "服務站被隱藏",
+  "account.domain_blocked": "服務站被封鎖",
   "account.edit_profile": "修改個人資料",
   "account.enable_notifications": "如果 @{name} 發文請通知我",
-  "account.endorse": "在個人資料推薦對方",
-  "account.follow": "關注",
-  "account.followers": "關注的人",
-  "account.followers.empty": "尚沒有人關注這位使用者。",
-  "account.followers_counter": "被 {count, plural,one {{counter} 人}other {{counter} 人}}關注",
+  "account.endorse": "在個人資料頁推薦對方",
+  "account.follow": "正在關注",
+  "account.followers": "關注者",
+  "account.followers.empty": "尚未有人關注這位使用者。",
+  "account.followers_counter": "有 {count, plural,one {{counter} 個} other {{counter} 個}}關注者",
   "account.following_counter": "正在關注 {count, plural,one {{counter}}other {{counter} 人}}",
-  "account.follows.empty": "這位使用者尚未關注任何使用者。",
+  "account.follows.empty": "這位使用者尚未關注任何人。",
   "account.follows_you": "關注你",
   "account.hide_reblogs": "隱藏 @{name} 的轉推",
   "account.last_status": "上次活躍時間",
   "account.link_verified_on": "此連結的所有權已在 {date} 檢查過",
-  "account.locked_info": "此用戶的私隱狀態為不公開,關注請求需經過該用戶的審核。",
+  "account.locked_info": "這位使用者將私隱設定為「不公開」,會手動審批誰能關注他/她。",
   "account.media": "媒體",
   "account.mention": "提及 @{name}",
   "account.moved_to": "{name} 已經遷移到:",
@@ -38,15 +38,15 @@
   "account.requested": "等候審批",
   "account.share": "分享 @{name} 的個人資料",
   "account.show_reblogs": "顯示 @{name} 的推文",
-  "account.statuses_counter": "{count, plural,one {{counter} 則}other {{counter} 則}}嘟文",
+  "account.statuses_counter": "{count, plural,one {{counter} 篇}other {{counter} 篇}}文章",
   "account.unblock": "解除對 @{name} 的封鎖",
-  "account.unblock_domain": "不再隱藏 {domain}",
+  "account.unblock_domain": "解除對域名 {domain} 的封鎖",
   "account.unendorse": "不再於個人資料頁面推薦對方",
   "account.unfollow": "取消關注",
   "account.unmute": "取消 @{name} 的靜音",
   "account.unmute_notifications": "取消來自 @{name} 通知的靜音",
   "account_note.placeholder": "按此添加備注",
-  "alert.rate_limited.message": "請在 {retry_time, time, medium} 過後重試",
+  "alert.rate_limited.message": "請在 {retry_time, time, medium} 後重試",
   "alert.rate_limited.title": "已限速",
   "alert.unexpected.message": "發生不可預期的錯誤。",
   "alert.unexpected.title": "噢!",
@@ -59,12 +59,12 @@
   "bundle_modal_error.close": "關閉",
   "bundle_modal_error.message": "加載本組件出錯。",
   "bundle_modal_error.retry": "重試",
-  "column.blocks": "封鎖用戶",
+  "column.blocks": "封鎖名單",
   "column.bookmarks": "書籤",
   "column.community": "本站時間軸",
   "column.direct": "個人訊息",
   "column.directory": "瀏覽個人資料",
-  "column.domain_blocks": "隱藏的服務站",
+  "column.domain_blocks": "封鎖的服務站",
   "column.favourites": "最愛的文章",
   "column.follow_requests": "關注請求",
   "column.home": "主頁",
@@ -77,25 +77,25 @@
   "column_header.hide_settings": "隱藏設定",
   "column_header.moveLeft_settings": "將欄左移",
   "column_header.moveRight_settings": "將欄右移",
-  "column_header.pin": "固定",
+  "column_header.pin": "置頂",
   "column_header.show_settings": "顯示設定",
-  "column_header.unpin": "取下",
+  "column_header.unpin": "取消置頂",
   "column_subheading.settings": "設定",
   "community.column_settings.local_only": "只顯示本站",
-  "community.column_settings.media_only": "僅媒體",
+  "community.column_settings.media_only": "只顯示多媒體",
   "community.column_settings.remote_only": "只顯示外站",
-  "compose_form.direct_message_warning": "這文章只有被提及的用戶才可以看到。",
+  "compose_form.direct_message_warning": "這文章只有被提及的使用者才可以看到。",
   "compose_form.direct_message_warning_learn_more": "了解更多",
   "compose_form.hashtag_warning": "這文章因為不是公開,所以不會被標籤搜索。只有公開的文章才會被標籤搜索。",
-  "compose_form.lock_disclaimer": "你的用戶狀態為「{locked}」,任何人都能立即關注你,然後看到「只有關注者能看」的文章。",
-  "compose_form.lock_disclaimer.lock": "公共",
+  "compose_form.lock_disclaimer": "你的用戶狀態沒有{locked},任何人都能立即關注你,然後看到「只有關注者能看」的文章。",
+  "compose_form.lock_disclaimer.lock": "鎖定",
   "compose_form.placeholder": "你在想甚麼?",
   "compose_form.poll.add_option": "新增選擇",
   "compose_form.poll.duration": "投票期限",
   "compose_form.poll.option_placeholder": "第 {number} 個選擇",
   "compose_form.poll.remove_option": "移除此選擇",
   "compose_form.poll.switch_to_multiple": "變更投票為允許多個選項",
-  "compose_form.poll.switch_to_single": "變更投票為允許單一選項",
+  "compose_form.poll.switch_to_single": "變更投票為限定單一選項",
   "compose_form.publish": "發文",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "標記媒體為敏感內容",
@@ -112,8 +112,8 @@
   "confirmations.delete.message": "你確定要刪除這文章嗎?",
   "confirmations.delete_list.confirm": "刪除",
   "confirmations.delete_list.message": "你確定要永久刪除這列表嗎?",
-  "confirmations.domain_block.confirm": "隱藏整個網站",
-  "confirmations.domain_block.message": "你真的真的確定要隱藏整個 {domain} ?多數情況下,比較推薦封鎖或靜音幾個特定目標就好。你從此將不會再看到該站的內容和通知。來自該站的關注者亦會被移除。",
+  "confirmations.domain_block.confirm": "封鎖整個網站",
+  "confirmations.domain_block.message": "你真的真的確定要封鎖整個 {domain} ?多數情況下,封鎖或靜音幾個特定目標就已經有效,也是比較建議的做法。若然封鎖全站,你將不會再在這裏看到該站的內容和通知。來自該站的關注者亦會被移除。",
   "confirmations.logout.confirm": "登出",
   "confirmations.logout.message": "確定要登出嗎?",
   "confirmations.mute.confirm": "靜音",
@@ -129,9 +129,9 @@
   "conversation.mark_as_read": "標為已讀",
   "conversation.open": "檢視對話",
   "conversation.with": "與 {names}",
-  "directory.federated": "來自已知聯邦宇宙",
+  "directory.federated": "來自已知的聯盟網絡",
   "directory.local": "僅來自 {domain}",
-  "directory.new_arrivals": "新貨",
+  "directory.new_arrivals": "新內容",
   "directory.recently_active": "最近活躍",
   "embed.instructions": "要內嵌此文章,請將以下代碼貼進你的網站。",
   "embed.preview": "看上去會是這樣:",
@@ -141,7 +141,7 @@
   "emoji_button.food": "飲飲食食",
   "emoji_button.label": "加入表情符號",
   "emoji_button.nature": "自然",
-  "emoji_button.not_found": "沒有表情符號!! (╯°□°)╯︵ ┻━┻",
+  "emoji_button.not_found": "沒找到表情符號!! (╯°□°)╯︵ ┻━┻",
   "emoji_button.objects": "物品",
   "emoji_button.people": "人物",
   "emoji_button.recent": "常用",
@@ -149,6 +149,7 @@
   "emoji_button.search_results": "搜尋結果",
   "emoji_button.symbols": "符號",
   "emoji_button.travel": "旅遊景物",
+  "empty_column.account_suspended": "帳號已停權",
   "empty_column.account_timeline": "這裡還沒有嘟文!",
   "empty_column.account_unavailable": "無法取得個人資料",
   "empty_column.blocks": "你還沒有封鎖任何使用者。",
@@ -156,11 +157,11 @@
   "empty_column.community": "本站時間軸暫時未有內容,快寫一點東西來搶頭香啊!",
   "empty_column.direct": "你沒有個人訊息。當你發出或接收個人訊息,就會在這裡出現。",
   "empty_column.domain_blocks": "尚未隱藏任何網域。",
-  "empty_column.favourited_statuses": "你還沒收藏任何嘟文。這裡將會顯示你收藏的嘟文。",
-  "empty_column.favourites": "還沒有人收藏這則嘟文。這裡將會顯示被收藏的嘟文。",
+  "empty_column.favourited_statuses": "你還沒收藏任何文章。這裡將會顯示你收藏的嘟文。",
+  "empty_column.favourites": "還沒有人收藏這則文章。這裡將會顯示被收藏的嘟文。",
   "empty_column.follow_requests": "您尚未收到任何關注請求。這裡將會顯示收到的關注請求。",
   "empty_column.hashtag": "這個標籤暫時未有內容。",
-  "empty_column.home": "你還沒有關注任何用戶。快看看{public},向其他用戶搭訕吧。",
+  "empty_column.home": "你還沒有關注任何使用者。快看看{public},向其他使用者搭訕吧。",
   "empty_column.home.public_timeline": "公共時間軸",
   "empty_column.list": "這個列表暫時未有內容。",
   "empty_column.lists": "你還沒有建立任何名單。這裡將會顯示你所建立的名單。",
@@ -171,7 +172,7 @@
   "error.unexpected_crash.explanation_addons": "此頁面無法被正確顯示,這可能是由於瀏覽器的附加元件或網頁自動翻譯工具造成的。",
   "error.unexpected_crash.next_steps": "請嘗試重新整理頁面。如果狀況沒有進展,你可以使用不同的瀏覽器或 Mastodon 應用程式來檢視。",
   "error.unexpected_crash.next_steps_addons": "請嘗試停止使用這些附加元件然後重新載入頁面。如果問題沒有解決,你仍然可以使用不同的瀏覽器或 Mastodon 應用程式來檢視。",
-  "errors.unexpected_crash.copy_stacktrace": "複製到剪貼簿",
+  "errors.unexpected_crash.copy_stacktrace": "複製 stacktrace 到剪貼簿",
   "errors.unexpected_crash.report_issue": "舉報問題",
   "follow_request.authorize": "批准",
   "follow_request.reject": "拒絕",
@@ -182,8 +183,8 @@
   "getting_started.documentation": "文件",
   "getting_started.heading": "開始使用",
   "getting_started.invite": "邀請使用者",
-  "getting_started.open_source_notice": "Mastodon(萬象)是一個開放源碼的軟件。你可以在官方 GitHub ({github}) 貢獻或者回報問題。",
-  "getting_started.security": "帳戶安全",
+  "getting_started.open_source_notice": "Mastodon(萬象)是一個開放源碼的軟件。你可以在官方 GitHub {github} 貢獻或者回報問題。",
+  "getting_started.security": "帳戶設定",
   "getting_started.terms": "服務條款",
   "hashtag.column_header.tag_mode.all": "以及{additional}",
   "hashtag.column_header.tag_mode.any": "或是{additional}",
@@ -193,7 +194,7 @@
   "hashtag.column_settings.tag_mode.all": "全部",
   "hashtag.column_settings.tag_mode.any": "任一",
   "hashtag.column_settings.tag_mode.none": "全不",
-  "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
+  "hashtag.column_settings.tag_toggle": "在這欄位加入額外的標籤",
   "home.column_settings.basic": "基本",
   "home.column_settings.show_reblogs": "顯示被轉推的文章",
   "home.column_settings.show_replies": "顯示回應文章",
@@ -203,24 +204,24 @@
   "intervals.full.hours": "{number, plural, one {# 小時} other {# 小時}}",
   "intervals.full.minutes": "{number, plural, one {# 分鐘} other {# 分鐘}}",
   "introduction.federation.action": "下一步",
-  "introduction.federation.federated.headline": "站台聯盟",
-  "introduction.federation.federated.text": "來自聯盟宇宙中其他站台的公開嘟文將會在站點聯盟時間軸中顯示。",
+  "introduction.federation.federated.headline": "已知服務站",
+  "introduction.federation.federated.text": "來自社交聯盟其他網站的公開文章,將會顯示在站點跨站時間軸上。",
   "introduction.federation.home.headline": "首頁",
-  "introduction.federation.home.text": "你關注使用者的嘟文將會在首頁動態中顯示。你可以關注任何伺服器上的任何人!",
-  "introduction.federation.local.headline": "本機",
-  "introduction.federation.local.text": "跟您同伺服器之使用者所發的公開嘟文將會顯示在本機時間軸中。",
-  "introduction.interactions.action": "Finish toot-orial!",
-  "introduction.interactions.favourite.headline": "關注",
-  "introduction.interactions.favourite.text": "您能儲存嘟文供稍候觀看,或者收藏嘟文,讓作者知道您喜歡這則嘟文。",
-  "introduction.interactions.reblog.headline": "轉嘟",
-  "introduction.interactions.reblog.text": "您能藉由轉嘟他人嘟文來分享給您的關注者。",
+  "introduction.federation.home.text": "你所關注使用者的文章,將會在首頁動態中顯示。你可以關注任何伺服器上的任何人!",
+  "introduction.federation.local.headline": "本站",
+  "introduction.federation.local.text": "與你共用一個服務站使用者的公開文章,將會顯示在本站時間軸中。",
+  "introduction.interactions.action": "完成使用教學",
+  "introduction.interactions.favourite.headline": "最愛",
+  "introduction.interactions.favourite.text": "你能用將文章加入「最愛的文章」,一方便把儲存文章稍候觀看,也同時對作者表示支持。",
+  "introduction.interactions.reblog.headline": "轉推",
+  "introduction.interactions.reblog.text": "你可以透過「轉推文章」把文章分享給你的關注者。",
   "introduction.interactions.reply.headline": "回覆",
-  "introduction.interactions.reply.text": "您能回覆其他人或自己的嘟文,這麼做會把這些回覆串成一串對話。",
+  "introduction.interactions.reply.text": "你可以回覆其他人或自己的文章,這麼做會把這些回覆串成一串對話。",
   "introduction.welcome.action": "開始旅程吧!",
   "introduction.welcome.headline": "第一步",
-  "introduction.welcome.text": "歡迎來到聯盟宇宙!等等你就可以廣播訊息及跨越各種各式各樣的伺服器與朋友聊天。但這台伺服器,{domain},非常特別 - 它寄管了你的個人資料,所以請記住它的名字。",
-  "keyboard_shortcuts.back": "後退",
-  "keyboard_shortcuts.blocked": "開啟「封鎖使用者」名單",
+  "introduction.welcome.text": "歡迎來到聯盟社交網絡 (fediverse)!稍後你就可以廣播訊息,同時與多個服務站的不同朋友聊天。但留意 {domain} 這個服務站非常特別——它託管了你的個人資料啊!所以請記住它。",
+  "keyboard_shortcuts.back": "返回",
+  "keyboard_shortcuts.blocked": "開啟封鎖名單",
   "keyboard_shortcuts.boost": "轉推",
   "keyboard_shortcuts.column": "把標示移動到其中一列",
   "keyboard_shortcuts.compose": "把標示移動到文字輸入區",
@@ -228,20 +229,20 @@
   "keyboard_shortcuts.direct": "開啟私訊欄",
   "keyboard_shortcuts.down": "在列表往下移動",
   "keyboard_shortcuts.enter": "打開文章",
-  "keyboard_shortcuts.favourite": "收藏",
-  "keyboard_shortcuts.favourites": "開啟收藏名單",
-  "keyboard_shortcuts.federated": "開啟站點聯盟時間軸",
+  "keyboard_shortcuts.favourite": "收藏文章",
+  "keyboard_shortcuts.favourites": "開啟最愛的內容",
+  "keyboard_shortcuts.federated": "打開跨站時間軸",
   "keyboard_shortcuts.heading": "鍵盤快速鍵",
-  "keyboard_shortcuts.home": "開啟首頁時間軸",
+  "keyboard_shortcuts.home": "開啟個人時間軸",
   "keyboard_shortcuts.hotkey": "快速鍵",
   "keyboard_shortcuts.legend": "顯示這個說明",
-  "keyboard_shortcuts.local": "開啟本機時間軸",
+  "keyboard_shortcuts.local": "開啟本站時間軸",
   "keyboard_shortcuts.mention": "提及作者",
-  "keyboard_shortcuts.muted": "開啟靜音使用者名單",
+  "keyboard_shortcuts.muted": "開啟靜音名單",
   "keyboard_shortcuts.my_profile": "開啟個人資料頁面",
   "keyboard_shortcuts.notifications": "開啟通知欄",
   "keyboard_shortcuts.open_media": "開啟媒體",
-  "keyboard_shortcuts.pinned": "開啟釘選的嘟文名單",
+  "keyboard_shortcuts.pinned": "開啟釘選的文章清單",
   "keyboard_shortcuts.profile": "開啟作者的個人資料頁面",
   "keyboard_shortcuts.reply": "回覆",
   "keyboard_shortcuts.requests": "開啟關注請求名單",
@@ -250,7 +251,7 @@
   "keyboard_shortcuts.start": "開啟「開始使用」欄位",
   "keyboard_shortcuts.toggle_hidden": "顯示或隱藏被標為敏感的文字",
   "keyboard_shortcuts.toggle_sensitivity": "顯示 / 隱藏媒體",
-  "keyboard_shortcuts.toot": "新的推文",
+  "keyboard_shortcuts.toot": "新的文章",
   "keyboard_shortcuts.unfocus": "把標示移離文字輸入和搜索",
   "keyboard_shortcuts.up": "在列表往上移動",
   "lightbox.close": "關閉",
@@ -258,7 +259,6 @@
   "lightbox.expand": "擴大檢視",
   "lightbox.next": "下一頁",
   "lightbox.previous": "上一頁",
-  "lightbox.view_context": "檢視內文",
   "lists.account.add": "新增到列表",
   "lists.account.remove": "從列表刪除",
   "lists.delete": "刪除列表",
@@ -266,28 +266,28 @@
   "lists.edit.submit": "變更標題",
   "lists.new.create": "新增列表",
   "lists.new.title_placeholder": "新列表標題",
-  "lists.replies_policy.all_replies": "任何關注的用戶",
-  "lists.replies_policy.list_replies": "列表中的用戶",
-  "lists.replies_policy.no_replies": "沒有人",
+  "lists.replies_policy.followed": "任何已關注的用戶",
+  "lists.replies_policy.list": "列表中的用戶",
+  "lists.replies_policy.none": "無人",
   "lists.replies_policy.title": "顯示回應文章︰",
-  "lists.search": "從你關注的用戶中搜索",
+  "lists.search": "從你關注的人搜索",
   "lists.subheading": "列表",
   "load_pending": "{count, plural, other {# 個新項目}}",
   "loading_indicator.label": "載入中...",
-  "media_gallery.toggle_visible": "打開或關上",
+  "media_gallery.toggle_visible": "隱藏圖片",
   "missing_indicator.label": "找不到內容",
   "missing_indicator.sublabel": "無法找到內容",
   "mute_modal.duration": "時間",
-  "mute_modal.hide_notifications": "隱藏來自這用戶的通知嗎?",
+  "mute_modal.hide_notifications": "需要隱藏這使用者的通知嗎?",
   "mute_modal.indefinite": "沒期限",
-  "navigation_bar.apps": "封鎖的使用者",
-  "navigation_bar.blocks": "被你封鎖的用戶",
+  "navigation_bar.apps": "手機 App",
+  "navigation_bar.blocks": "封鎖名單",
   "navigation_bar.bookmarks": "書籤",
   "navigation_bar.community_timeline": "本站時間軸",
-  "navigation_bar.compose": "撰寫新嘟文",
+  "navigation_bar.compose": "撰寫新文章",
   "navigation_bar.direct": "個人訊息",
   "navigation_bar.discover": "探索",
-  "navigation_bar.domain_blocks": "隱藏的服務站",
+  "navigation_bar.domain_blocks": "封鎖的服務站",
   "navigation_bar.edit_profile": "修改個人資料",
   "navigation_bar.favourites": "最愛的內容",
   "navigation_bar.filters": "靜音詞彙",
@@ -297,24 +297,24 @@
   "navigation_bar.keyboard_shortcuts": "鍵盤快速鍵",
   "navigation_bar.lists": "列表",
   "navigation_bar.logout": "登出",
-  "navigation_bar.mutes": "被你靜音的用戶",
+  "navigation_bar.mutes": "靜音名單",
   "navigation_bar.personal": "個人",
   "navigation_bar.pins": "置頂文章",
   "navigation_bar.preferences": "偏好設定",
   "navigation_bar.public_timeline": "跨站時間軸",
   "navigation_bar.security": "安全",
-  "notification.favourite": "{name} 收藏了你的文章",
+  "notification.favourite": "{name} 喜歡你的文章",
   "notification.follow": "{name} 開始關注你",
   "notification.follow_request": "{name} 要求關注你",
   "notification.mention": "{name} 提及你",
-  "notification.own_poll": "您的投票已結束",
-  "notification.poll": "您投過的投票已經結束",
+  "notification.own_poll": "你的投票已結束",
+  "notification.poll": "你參與過的一個投票已經結束",
   "notification.reblog": "{name} 轉推你的文章",
-  "notification.status": "{name} 剛剛發了嘟文",
+  "notification.status": "{name} 剛發表了文章",
   "notifications.clear": "清空通知紀錄",
   "notifications.clear_confirmation": "你確定要清空通知紀錄嗎?",
   "notifications.column_settings.alert": "顯示桌面通知",
-  "notifications.column_settings.favourite": "收藏了你的文章:",
+  "notifications.column_settings.favourite": "你最愛的文章:",
   "notifications.column_settings.filter_bar.advanced": "顯示所有分類",
   "notifications.column_settings.filter_bar.category": "快速過濾欄",
   "notifications.column_settings.filter_bar.show": "顯示",
@@ -328,32 +328,34 @@
   "notifications.column_settings.sound": "播放音效",
   "notifications.column_settings.status": "新的文章",
   "notifications.filter.all": "全部",
-  "notifications.filter.boosts": "轉嘟",
+  "notifications.filter.boosts": "轉推",
   "notifications.filter.favourites": "最愛",
   "notifications.filter.follows": "關注的使用者",
   "notifications.filter.mentions": "提及",
   "notifications.filter.polls": "投票結果",
   "notifications.filter.statuses": "已關注的用戶的最新動態",
+  "notifications.grant_permission": "授予權限",
   "notifications.group": "{count} 條通知",
   "notifications.mark_as_read": "標記所有通知為已讀",
-  "notifications.permission_denied": "瀏覽器的桌面通知權限設定為拒絕,因此不可以啟用桌面通知",
-  "notifications.permission_denied_alert": "瀏覽器的桌面通知權限設定為拒絕,因此不可以啟用桌面通知",
+  "notifications.permission_denied": "本站不能發送桌面通知,因為瀏覽器先前拒絕了本站的桌面通知權限請求",
+  "notifications.permission_denied_alert": "無法啟用桌面通知,因為瀏覽器先前拒絕了本站的桌面通知權限請求",
+  "notifications.permission_required": "由於瀏覽器未有授予桌面通知權限,本站暫未能發送桌面通知。",
   "notifications_permission_banner.enable": "啟用桌面通知",
-  "notifications_permission_banner.how_to_control": "啟用桌面通知可以在 Mastodon 沒有開啟的時候接收通知。在已經啟用桌面通知的時候,你可以透過上面的 {icon} 按鈕準確控制哪些類型的互動會產生桌面通知。",
-  "notifications_permission_banner.title": "不放過任何通知",
+  "notifications_permission_banner.how_to_control": "只要啟用桌面通知,便可在 Mastodon 網站沒有打開時收到通知。在已經啟用桌面通知的時候,你可以透過上面的 {icon} 按鈕準確控制哪些類型的互動會產生桌面通知。",
+  "notifications_permission_banner.title": "不放過任何事情",
   "picture_in_picture.restore": "還原影片播放器",
   "poll.closed": "已關閉",
   "poll.refresh": "重新整理",
-  "poll.total_people": "{count, plural, one {# 個投票} other {# 個投票}}",
-  "poll.total_votes": "{count, plural, one {# 個投票} other {# 個投票}}",
+  "poll.total_people": "{count, plural, one {# 人投票} other {# 人投票}}",
+  "poll.total_votes": "{count, plural, one {# 票} other {# 票}}",
   "poll.vote": "投票",
-  "poll.voted": "你已對此問題投票",
+  "poll.voted": "你已投票給這答案",
   "poll_button.add_poll": "建立投票",
   "poll_button.remove_poll": "移除投票",
   "privacy.change": "調整私隱設定",
-  "privacy.direct.long": "只有提及的用戶能看到",
+  "privacy.direct.long": "只有提及的使用者能看到",
   "privacy.direct.short": "私人訊息",
-  "privacy.private.long": "只有關注你用戶能看到",
+  "privacy.private.long": "只有你的關注者能看到",
   "privacy.private.short": "關注者",
   "privacy.public.long": "在公共時間軸顯示",
   "privacy.public.short": "公共",
@@ -362,11 +364,11 @@
   "refresh": "重新整理",
   "regeneration_indicator.label": "載入中……",
   "regeneration_indicator.sublabel": "你的主頁時間軸正在準備中!",
-  "relative_time.days": "{number}日",
-  "relative_time.hours": "{number}小時",
+  "relative_time.days": "{number}日前",
+  "relative_time.hours": "{number}小時前",
   "relative_time.just_now": "剛剛",
-  "relative_time.minutes": "{number}分鐘",
-  "relative_time.seconds": "{number}秒",
+  "relative_time.minutes": "{number}分鐘前",
+  "relative_time.seconds": "{number}秒前",
   "relative_time.today": "今天",
   "reply_indicator.cancel": "取消",
   "report.forward": "轉寄到 {target}",
@@ -377,28 +379,28 @@
   "report.target": "舉報",
   "search.placeholder": "搜尋",
   "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.user": "用戶",
+  "search_popout.tips.text": "輸入簡單的文字,搜索符合的顯示名稱、使用者名稱和標籤",
+  "search_popout.tips.user": "使用者",
   "search_results.accounts": "使用者",
   "search_results.hashtags": "標籤",
   "search_results.statuses": "文章",
-  "search_results.statuses_fts_disabled": "「依內容搜尋嘟文」未在此 Mastodon 伺服器啟用。",
+  "search_results.statuses_fts_disabled": "此 Mastodon 伺服器並未啟用「搜尋文章內章」功能。",
   "search_results.total": "{count, number} 項結果",
   "status.admin_account": "開啟 @{name} 的管理介面",
-  "status.admin_status": "在管理介面開啟此嘟文",
+  "status.admin_status": "在管理介面開啟這篇文章",
   "status.block": "封鎖 @{name}",
   "status.bookmark": "書籤",
   "status.cancel_reblog_private": "取消轉推",
   "status.cannot_reblog": "這篇文章無法被轉推",
-  "status.copy": "將連結複製到嘟文中",
+  "status.copy": "將連結複製到文章中",
   "status.delete": "刪除",
-  "status.detailed_status": "對話的詳細內容",
+  "status.detailed_status": "詳細對話內容",
   "status.direct": "私訊 @{name}",
-  "status.embed": "鑲嵌",
-  "status.favourite": "收藏",
+  "status.embed": "嵌入",
+  "status.favourite": "最愛",
   "status.filtered": "已過濾",
   "status.load_more": "載入更多",
   "status.media_hidden": "隱藏媒體內容",
@@ -413,7 +415,7 @@
   "status.reblog": "轉推",
   "status.reblog_private": "轉推到原讀者",
   "status.reblogged_by": "{name} 轉推",
-  "status.reblogs.empty": "還沒有人轉嘟。如果有,會顯示在這裡。",
+  "status.reblogs.empty": "還未有人轉推。有的話會顯示在這裡。",
   "status.redraft": "刪除並編輯",
   "status.remove_bookmark": "移除書籤",
   "status.reply": "回應",
@@ -421,38 +423,38 @@
   "status.report": "舉報 @{name}",
   "status.sensitive_warning": "敏感內容",
   "status.share": "分享",
-  "status.show_less": "減少顯示",
-  "status.show_less_all": "減少顯示這類文章",
-  "status.show_more": "顯示更多",
-  "status.show_more_all": "顯示更多這類文章",
+  "status.show_less": "收起",
+  "status.show_less_all": "全部收起",
+  "status.show_more": "展開",
+  "status.show_more_all": "全部展開",
   "status.show_thread": "顯示討論串",
   "status.uncached_media_warning": "無法使用",
-  "status.unmute_conversation": "解禁對話",
+  "status.unmute_conversation": "對話解除靜音",
   "status.unpin": "解除置頂",
   "suggestions.dismiss": "關閉建議",
-  "suggestions.header": "您可能對這些東西有興趣…",
+  "suggestions.header": "你可能對這些感興趣…",
   "tabs_bar.federated_timeline": "跨站",
   "tabs_bar.home": "主頁",
   "tabs_bar.local_timeline": "本站",
   "tabs_bar.notifications": "通知",
   "tabs_bar.search": "搜尋",
-  "time_remaining.days": "剩餘{number, plural, one {# 天數} other {# 天數}}",
-  "time_remaining.hours": "剩餘{number, plural, one {# 小時} other {# 小時}}",
-  "time_remaining.minutes": "剩餘{number, plural, one {# 分鐘} other {# 分鐘}}",
+  "time_remaining.days": "剩餘 {number, plural, one {# 天} other {# 天}}",
+  "time_remaining.hours": "剩餘 {number, plural, one {# 小時} other {# 小時}}",
+  "time_remaining.minutes": "剩餘 {number, plural, one {# 分鐘} other {# 分鐘}}",
   "time_remaining.moments": "剩餘時間",
   "time_remaining.seconds": "剩餘 {number, plural, one {# 秒} other {# 秒}}",
-  "timeline_hint.remote_resource_not_displayed": "不會顯示來自其他服務器的 {resource}",
+  "timeline_hint.remote_resource_not_displayed": "不會顯示來自其他伺服器的 {resource}",
   "timeline_hint.resources.followers": "關注者",
   "timeline_hint.resources.follows": "關注中",
-  "timeline_hint.resources.statuses": "更早的嘟文",
-  "trends.counter_by_accounts": "{count, plural,one {{counter} 人}other {{counter} 人}}正在討論",
-  "trends.trending_now": "目前趨勢",
+  "timeline_hint.resources.statuses": "更早的文章",
+  "trends.counter_by_accounts": "{count, plural, one {{counter} 個人}other {{counter} 個人}}正在討論",
+  "trends.trending_now": "現在流行",
   "ui.beforeunload": "如果你現在離開 Mastodon,你的草稿內容將會被丟棄。",
   "units.short.billion": "{count}B",
   "units.short.million": "{count}M",
   "units.short.thousand": "{count}K",
   "upload_area.title": "將檔案拖放至此上載",
-  "upload_button.label": "上載媒體檔案",
+  "upload_button.label": "加入圖片、影片或音訊檔",
   "upload_error.limit": "已達到檔案上傳限制。",
   "upload_error.poll": "不允許在投票上傳檔案。",
   "upload_form.audio_description": "簡單描述內容給聽障人士",
@@ -464,7 +466,7 @@
   "upload_modal.analyzing_picture": "正在分析圖片…",
   "upload_modal.apply": "套用",
   "upload_modal.choose_image": "選擇圖片",
-  "upload_modal.description_placeholder": "A quick brown fox 跳過那隻懶狗",
+  "upload_modal.description_placeholder": "一隻敏捷的狐狸,輕巧地跳過那隻懶洋洋的狗",
   "upload_modal.detect_text": "從圖片偵測文字",
   "upload_modal.edit_media": "編輯媒體",
   "upload_modal.hint": "點擊或拖曳圓圈以選擇預覽縮圖。",
@@ -473,9 +475,9 @@
   "upload_progress.label": "上載中……",
   "video.close": "關閉影片",
   "video.download": "下載檔案",
-  "video.exit_fullscreen": "退出全熒幕",
+  "video.exit_fullscreen": "退出全螢幕",
   "video.expand": "展開影片",
-  "video.fullscreen": "全熒幕",
+  "video.fullscreen": "全螢幕",
   "video.hide": "隱藏影片",
   "video.mute": "靜音",
   "video.pause": "暫停",
diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json
index a38a7c07a..cc996e91b 100644
--- a/app/javascript/mastodon/locales/zh-TW.json
+++ b/app/javascript/mastodon/locales/zh-TW.json
@@ -4,27 +4,27 @@
   "account.badges.bot": "機器人",
   "account.badges.group": "群組",
   "account.block": "封鎖 @{name}",
-  "account.block_domain": "隱藏來自 {domain} 的所有內容",
+  "account.block_domain": "封鎖來自 {domain} 網域的所有內容",
   "account.blocked": "已封鎖",
-  "account.browse_more_on_origin_server": "在該服務器的個人檔案頁上瀏覽更多",
+  "account.browse_more_on_origin_server": "在該伺服器的個人檔案頁上瀏覽更多",
   "account.cancel_follow_request": "取消關注請求",
   "account.direct": "傳私訊給 @{name}",
-  "account.disable_notifications": "當 @{name} 嘟文時不要再通知我",
-  "account.domain_blocked": "已隱藏網域",
+  "account.disable_notifications": "取消來自 @{name} 嘟文的通知",
+  "account.domain_blocked": "已封鎖網域",
   "account.edit_profile": "編輯個人資料",
   "account.enable_notifications": "當 @{name} 嘟文時通知我",
   "account.endorse": "在個人資料推薦對方",
   "account.follow": "關注",
   "account.followers": "關注者",
-  "account.followers.empty": "尚沒有人關注這位使用者。",
+  "account.followers.empty": "尚未有人關注這位使用者。",
   "account.followers_counter": "被 {count, plural,one {{counter} 人}other {{counter} 人}}關注",
   "account.following_counter": "正在關注 {count, plural,one {{counter}}other {{counter} 人}}",
-  "account.follows.empty": "這位使用者尚未關注任何使用者。",
-  "account.follows_you": "關注了你",
-  "account.hide_reblogs": "隱藏來自 @{name} 的轉推",
-  "account.last_status": "上次活躍",
+  "account.follows.empty": "這位使用者尚未關注任何人。",
+  "account.follows_you": "關注了您",
+  "account.hide_reblogs": "隱藏來自 @{name} 的轉嘟",
+  "account.last_status": "上次活躍時間",
   "account.link_verified_on": "已在 {date} 檢查此連結的擁有者權限",
-  "account.locked_info": "這隻帳戶的隱私狀態被設成鎖定。該擁有者會手動審核能關注這隻帳號的人。",
+  "account.locked_info": "此帳號的隱私狀態被設為鎖定。該擁有者會手動審核能關注此帳號的人。",
   "account.media": "媒體",
   "account.mention": "提及 @{name}",
   "account.moved_to": "{name} 已遷移至:",
@@ -40,13 +40,13 @@
   "account.show_reblogs": "顯示來自 @{name} 的嘟文",
   "account.statuses_counter": "{count, plural,one {{counter} 則}other {{counter} 則}}嘟文",
   "account.unblock": "取消封鎖 @{name}",
-  "account.unblock_domain": "取消隱藏 {domain}",
+  "account.unblock_domain": "取消封鎖域名 {domain}",
   "account.unendorse": "不再於個人資料頁面推薦對方",
   "account.unfollow": "取消關注",
   "account.unmute": "取消靜音 @{name}",
   "account.unmute_notifications": "重新接收來自 @{name} 的通知",
   "account_note.placeholder": "按此添加備注",
-  "alert.rate_limited.message": "請在 {retry_time, time, medium} 過後重試",
+  "alert.rate_limited.message": "請在 {retry_time, time, medium} 後重試",
   "alert.rate_limited.title": "已限速",
   "alert.unexpected.message": "發生了非預期的錯誤。",
   "alert.unexpected.title": "哎呀!",
@@ -59,17 +59,17 @@
   "bundle_modal_error.close": "關閉",
   "bundle_modal_error.message": "載入此元件時發生錯誤。",
   "bundle_modal_error.retry": "重試",
-  "column.blocks": "封鎖的使用者",
+  "column.blocks": "已封鎖的使用者",
   "column.bookmarks": "書籤",
   "column.community": "本機時間軸",
   "column.direct": "私訊",
   "column.directory": "瀏覽個人資料",
-  "column.domain_blocks": "隱藏的網域",
+  "column.domain_blocks": "已封鎖的網域",
   "column.favourites": "收藏",
   "column.follow_requests": "關注請求",
-  "column.home": "主頁",
+  "column.home": "首頁",
   "column.lists": "名單",
-  "column.mutes": "被靜音的使用者",
+  "column.mutes": "已靜音的使用者",
   "column.notifications": "通知",
   "column.pins": "釘選的嘟文",
   "column.public": "聯邦時間軸",
@@ -81,115 +81,116 @@
   "column_header.show_settings": "顯示設定",
   "column_header.unpin": "取消釘選",
   "column_subheading.settings": "設定",
-  "community.column_settings.local_only": "只有本地",
+  "community.column_settings.local_only": "只有本機",
   "community.column_settings.media_only": "只有媒體",
   "community.column_settings.remote_only": "只有遠端",
   "compose_form.direct_message_warning": "這條嘟文只有被提及的使用者才看得到。",
   "compose_form.direct_message_warning_learn_more": "了解更多",
-  "compose_form.hashtag_warning": "由於這則嘟文被設定成「不公開」,所以它將不會被列在任何主題標籤下。只有公開的嘟文才能藉主題標籤找到。",
+  "compose_form.hashtag_warning": "由於這則嘟文設定為「不公開」,它將不會被列於任何主題標籤下。只有公開的嘟文才能藉由主題標籤找到。",
   "compose_form.lock_disclaimer": "您的帳戶尚未{locked}。任何人都能關注您並看到您設定成只有關注者能看的嘟文。",
   "compose_form.lock_disclaimer.lock": "上鎖",
-  "compose_form.placeholder": "您正在想些什麼?",
-  "compose_form.poll.add_option": "新增選擇",
+  "compose_form.placeholder": "正在想些什麼嗎?",
+  "compose_form.poll.add_option": "新增選項",
   "compose_form.poll.duration": "投票期限",
-  "compose_form.poll.option_placeholder": "第 {number} 個選擇",
-  "compose_form.poll.remove_option": "移除此選擇",
+  "compose_form.poll.option_placeholder": "第 {number} 個選項",
+  "compose_form.poll.remove_option": "移除此選項",
   "compose_form.poll.switch_to_multiple": "變更投票為允許多個選項",
   "compose_form.poll.switch_to_single": "變更投票為允許單一選項",
   "compose_form.publish": "嘟出去",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "標記媒體為敏感內容",
   "compose_form.sensitive.marked": "此媒體被標記為敏感內容",
-  "compose_form.sensitive.unmarked": "此媒體未標記為敏感內容",
+  "compose_form.sensitive.unmarked": "此媒體未被標記為敏感內容",
   "compose_form.spoiler.marked": "正文已隱藏到警告之後",
   "compose_form.spoiler.unmarked": "正文未被隱藏",
   "compose_form.spoiler_placeholder": "請在此處寫入警告訊息",
   "confirmation_modal.cancel": "取消",
   "confirmations.block.block_and_report": "封鎖並檢舉",
   "confirmations.block.confirm": "封鎖",
-  "confirmations.block.message": "確定封鎖 {name} ?",
+  "confirmations.block.message": "確定要封鎖 {name} 嗎?",
   "confirmations.delete.confirm": "刪除",
-  "confirmations.delete.message": "你確定要刪除這條嘟文?",
+  "confirmations.delete.message": "您確定要刪除這則嘟文?",
   "confirmations.delete_list.confirm": "刪除",
   "confirmations.delete_list.message": "確定永久刪除此名單?",
-  "confirmations.domain_block.confirm": "隱藏整個網域",
-  "confirmations.domain_block.message": "真的非常確定封鎖整個 {domain} 嗎?大部分情況下,你只需要封鎖或靜音少數特定的人就能滿足需求了。你將不能在任何公開的時間軸及通知中看到那個網域的內容。你來自該網域的關注者也會被移除。",
+  "confirmations.domain_block.confirm": "隱藏整個域名",
+  "confirmations.domain_block.message": "真的非常確定封鎖整個 {domain} 網域嗎?大部分情況下,您只需要封鎖或靜音少數特定的帳號就能滿足需求了。您將不能在任何公開的時間軸及通知中看到此網域的內容。您來自該網域的關注者也將被移除。",
   "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.unfollow.confirm": "取消關注",
-  "confirmations.unfollow.message": "真的要取消關注 {name} 嗎?",
+  "confirmations.unfollow.message": "確定要取消關注 {name} 嗎?",
   "conversation.delete": "刪除對話",
-  "conversation.mark_as_read": "標為已讀",
+  "conversation.mark_as_read": "標記為已讀",
   "conversation.open": "檢視對話",
   "conversation.with": "與 {names}",
   "directory.federated": "來自已知聯邦宇宙",
-  "directory.local": "僅來自 {domain}",
+  "directory.local": "僅來自 {domain} 網域",
   "directory.new_arrivals": "新人",
   "directory.recently_active": "最近活躍",
-  "embed.instructions": "要嵌入此嘟文,請將以下程式碼貼進你的網站。",
-  "embed.preview": "他會顯示成這樣:",
+  "embed.instructions": "要在您的網站嵌入此嘟文,請複製以下程式碼。",
+  "embed.preview": "它將顯示成這樣:",
   "emoji_button.activity": "活動",
   "emoji_button.custom": "自訂",
-  "emoji_button.flags": "旗標",
-  "emoji_button.food": "飲食",
+  "emoji_button.flags": "旗幟",
+  "emoji_button.food": "食物 & 飲料",
   "emoji_button.label": "插入表情符號",
-  "emoji_button.nature": "大自然",
+  "emoji_button.nature": "自然",
   "emoji_button.not_found": "啊就沒這表情符號吼!! (╯°□°)╯︵ ┻━┻",
   "emoji_button.objects": "物件",
-  "emoji_button.people": "使用者",
+  "emoji_button.people": "人物",
   "emoji_button.recent": "最常使用",
   "emoji_button.search": "搜尋…",
   "emoji_button.search_results": "搜尋結果",
   "emoji_button.symbols": "符號",
   "emoji_button.travel": "旅遊與地點",
+  "empty_column.account_suspended": "帳號被暫停",
   "empty_column.account_timeline": "這裡還沒有嘟文!",
   "empty_column.account_unavailable": "無法取得個人資料",
-  "empty_column.blocks": "你還沒有封鎖任何使用者。",
-  "empty_column.bookmarked_statuses": "你還沒建立任何書籤。這裡將會顯示你建立的書籤。",
-  "empty_column.community": "本地時間軸是空的。快公開嘟些文搶頭香啊!",
+  "empty_column.blocks": "您還沒有封鎖任何使用者。",
+  "empty_column.bookmarked_statuses": "您還沒建立任何書籤。當您建立書簽時,它將於此顯示。",
+  "empty_column.community": "本機時間軸是空的。快公開嘟些文搶頭香啊!",
   "empty_column.direct": "您還沒有任何私訊。當您私訊別人或收到私訊時,它將於此顯示。",
-  "empty_column.domain_blocks": "尚未隱藏任何網域。",
-  "empty_column.favourited_statuses": "你還沒收藏任何嘟文。這裡將會顯示你收藏的嘟文。",
-  "empty_column.favourites": "還沒有人收藏這則嘟文。這裡將會顯示被收藏的嘟文。",
+  "empty_column.domain_blocks": "尚未封鎖任何網域。",
+  "empty_column.favourited_statuses": "您還沒收藏過任何嘟文。當您收藏嘟文時,它將於此顯示。",
+  "empty_column.favourites": "還沒有人收藏過這則嘟文。當有人收藏嘟文時,它將於此顯示。",
   "empty_column.follow_requests": "您尚未收到任何關注請求。這裡將會顯示收到的關注請求。",
   "empty_column.hashtag": "這個主題標籤下什麼也沒有。",
   "empty_column.home": "您的首頁時間軸是空的!前往 {public} 或使用搜尋功能來認識其他人。",
   "empty_column.home.public_timeline": "公開時間軸",
   "empty_column.list": "這份名單還沒有東西。當此名單的成員嘟出了新的嘟文時,它們就會顯示於此。",
-  "empty_column.lists": "你還沒有建立任何名單。這裡將會顯示你所建立的名單。",
-  "empty_column.mutes": "你尚未靜音任何使用者。",
+  "empty_column.lists": "您還沒有建立任何名單。這裡將會顯示您所建立的名單。",
+  "empty_column.mutes": "您尚未靜音任何使用者。",
   "empty_column.notifications": "您尚未收到任何通知,和別人互動開啟對話吧。",
   "empty_column.public": "這裡什麼都沒有!嘗試寫些公開的嘟文,或著自己關注其他伺服器的使用者後就會有嘟文出現了",
-  "error.unexpected_crash.explanation": "由於發生系統故障或瀏覽器相容性問題,故無法正常顯示頁面。",
+  "error.unexpected_crash.explanation": "由於發生系統故障或瀏覽器相容性問題,無法正常顯示此頁面。",
   "error.unexpected_crash.explanation_addons": "此頁面無法被正常顯示,這可能是由瀏覽器附加元件或網頁自動翻譯工具造成的。",
-  "error.unexpected_crash.next_steps": "請嘗試重新整理頁面。如果狀況沒有進展,你可以使用不同的瀏覽器或 Mastodon 應用程式來檢視。",
-  "error.unexpected_crash.next_steps_addons": "請嘗試重新整理頁面。如果狀況沒有進展,您可以嘗試使用不同的瀏覽器或 Mastodon 應用程式來檢視。",
-  "errors.unexpected_crash.copy_stacktrace": "複製到剪貼簿",
-  "errors.unexpected_crash.report_issue": "舉報問題",
+  "error.unexpected_crash.next_steps": "請嘗試重新整理頁面。如果狀況沒有改善,您可以使用不同的瀏覽器或應用程式來檢視來使用 Mastodon。",
+  "error.unexpected_crash.next_steps_addons": "請嘗試關閉他們然後重新整理頁面。如果狀況沒有改善,您可以使用不同的瀏覽器或應用程式來檢視來使用 Mastodon。",
+  "errors.unexpected_crash.copy_stacktrace": "複製 stacktrace 到剪貼簿",
+  "errors.unexpected_crash.report_issue": "回報問題",
   "follow_request.authorize": "授權",
   "follow_request.reject": "拒絕",
-  "follow_requests.unlocked_explanation": "即便您的帳號未被鎖定,{domain} 的員工認為可能想要自己審核這些帳號的追蹤請求。",
+  "follow_requests.unlocked_explanation": "即便您的帳號未被鎖定,{domain} 的員工認為您可能想要自己審核這些帳號的追蹤請求。",
   "generic.saved": "已儲存",
   "getting_started.developers": "開發者",
   "getting_started.directory": "個人資料目錄",
   "getting_started.documentation": "文件",
   "getting_started.heading": "開始使用",
   "getting_started.invite": "邀請使用者",
-  "getting_started.open_source_notice": "Mastodon 是開源軟體。你可以在 GitHub {github} 上貢獻或是回報問題。",
-  "getting_started.security": "安全性",
+  "getting_started.open_source_notice": "Mastodon 是開源軟體。您可以在 GitHub {github} 上貢獻或是回報問題。",
+  "getting_started.security": "帳號安全性設定",
   "getting_started.terms": "服務條款",
-  "hashtag.column_header.tag_mode.all": "以及{additional}",
-  "hashtag.column_header.tag_mode.any": "或是{additional}",
-  "hashtag.column_header.tag_mode.none": "而無需{additional}",
+  "hashtag.column_header.tag_mode.all": "以及 {additional}",
+  "hashtag.column_header.tag_mode.any": "或是 {additional}",
+  "hashtag.column_header.tag_mode.none": "而無需 {additional}",
   "hashtag.column_settings.select.no_options_message": "找不到建議",
-  "hashtag.column_settings.select.placeholder": "輸入主題標籤…",
+  "hashtag.column_settings.select.placeholder": "請輸入主題標籤…",
   "hashtag.column_settings.tag_mode.all": "全部",
   "hashtag.column_settings.tag_mode.any": "任一",
   "hashtag.column_settings.tag_mode.none": "全不",
@@ -203,14 +204,14 @@
   "intervals.full.hours": "{number, plural, one {# 小時} other {# 小時}}",
   "intervals.full.minutes": "{number, plural, one {# 分鐘} other {# 分鐘}}",
   "introduction.federation.action": "下一步",
-  "introduction.federation.federated.headline": "站台聯盟",
-  "introduction.federation.federated.text": "來自聯盟宇宙中其他站台的公開嘟文將會在站點聯盟時間軸中顯示。",
+  "introduction.federation.federated.headline": "聯邦",
+  "introduction.federation.federated.text": "來自聯盟宇宙中其他伺服器的公開嘟文將會在聯邦時間軸中顯示。",
   "introduction.federation.home.headline": "首頁",
   "introduction.federation.home.text": "你關注使用者的嘟文將會在首頁動態中顯示。你可以關注任何伺服器上的任何人!",
   "introduction.federation.local.headline": "本機",
   "introduction.federation.local.text": "跟您同伺服器之使用者所發的公開嘟文將會顯示在本機時間軸中。",
   "introduction.interactions.action": "完成教學!",
-  "introduction.interactions.favourite.headline": "關注",
+  "introduction.interactions.favourite.headline": "收藏",
   "introduction.interactions.favourite.text": "您能儲存嘟文供稍候觀看,或者收藏嘟文,讓作者知道您喜歡這則嘟文。",
   "introduction.interactions.reblog.headline": "轉嘟",
   "introduction.interactions.reblog.text": "您能藉由轉嘟他人嘟文來分享給您的關注者。",
@@ -218,23 +219,23 @@
   "introduction.interactions.reply.text": "您能回覆其他人或自己的嘟文,這麼做會把這些回覆串成一串對話。",
   "introduction.welcome.action": "開始旅程吧!",
   "introduction.welcome.headline": "第一步",
-  "introduction.welcome.text": "歡迎來到聯盟宇宙!等等你就可以廣播訊息及跨越各種各式各樣的伺服器與朋友聊天。但這台伺服器,{domain},非常特別 - 它寄管了你的個人資料,所以請記住它的名字。",
+  "introduction.welcome.text": "歡迎來到聯盟宇宙!稍候您就可以廣播訊息及跨越各種各式各樣的伺服器與朋友聊天。但這台伺服器,{domain},非常特別 - 它承載了您的個人資料,所以請記住它的名字。",
   "keyboard_shortcuts.back": "返回上一頁",
   "keyboard_shortcuts.blocked": "開啟「封鎖使用者」名單",
   "keyboard_shortcuts.boost": "轉嘟",
   "keyboard_shortcuts.column": "將焦點放在其中一欄的嘟文",
   "keyboard_shortcuts.compose": "將焦點移至撰寫文字區塊",
-  "keyboard_shortcuts.description": "描述",
+  "keyboard_shortcuts.description": "說明",
   "keyboard_shortcuts.direct": "開啟私訊欄",
-  "keyboard_shortcuts.down": "往下移動名單項目",
+  "keyboard_shortcuts.down": "在名單中往下移動",
   "keyboard_shortcuts.enter": "檢視嘟文",
-  "keyboard_shortcuts.favourite": "收藏",
+  "keyboard_shortcuts.favourite": "加到收藏",
   "keyboard_shortcuts.favourites": "開啟收藏名單",
-  "keyboard_shortcuts.federated": "開啟站點聯盟時間軸",
+  "keyboard_shortcuts.federated": "開啟聯邦時間軸",
   "keyboard_shortcuts.heading": "鍵盤快速鍵",
   "keyboard_shortcuts.home": "開啟首頁時間軸",
   "keyboard_shortcuts.hotkey": "快速鍵",
-  "keyboard_shortcuts.legend": "顯示此列表",
+  "keyboard_shortcuts.legend": "顯示此圖例",
   "keyboard_shortcuts.local": "開啟本機時間軸",
   "keyboard_shortcuts.mention": "提及作者",
   "keyboard_shortcuts.muted": "開啟靜音使用者名單",
@@ -243,7 +244,7 @@
   "keyboard_shortcuts.open_media": "開啟媒體",
   "keyboard_shortcuts.pinned": "開啟釘選的嘟文名單",
   "keyboard_shortcuts.profile": "開啟作者的個人資料頁面",
-  "keyboard_shortcuts.reply": "回覆",
+  "keyboard_shortcuts.reply": "回應嘟文",
   "keyboard_shortcuts.requests": "開啟關注請求名單",
   "keyboard_shortcuts.search": "將焦點移至搜尋框",
   "keyboard_shortcuts.spoilers": "顯示或隱藏被折疊的正文",
@@ -252,13 +253,12 @@
   "keyboard_shortcuts.toggle_sensitivity": "顯示 / 隱藏媒體",
   "keyboard_shortcuts.toot": "開始發出新嘟文",
   "keyboard_shortcuts.unfocus": "取消輸入文字區塊 / 搜尋的焦點",
-  "keyboard_shortcuts.up": "往上移動名單項目",
+  "keyboard_shortcuts.up": "在名單中往上移動",
   "lightbox.close": "關閉",
   "lightbox.compress": "折疊圖片檢視框",
   "lightbox.expand": "展開圖片檢視框",
   "lightbox.next": "下一步",
   "lightbox.previous": "上一步",
-  "lightbox.view_context": "檢視內文",
   "lists.account.add": "新增至名單",
   "lists.account.remove": "從名單中移除",
   "lists.delete": "刪除名單",
@@ -266,13 +266,13 @@
   "lists.edit.submit": "變更標題",
   "lists.new.create": "新增名單",
   "lists.new.title_placeholder": "新名單標題",
-  "lists.replies_policy.all_replies": "任何跟隨的使用者",
-  "lists.replies_policy.list_replies": "列表成員",
-  "lists.replies_policy.no_replies": "沒有人",
-  "lists.replies_policy.title": "顯示回覆",
+  "lists.replies_policy.followed": "任何跟隨的使用者",
+  "lists.replies_policy.list": "列表成員",
+  "lists.replies_policy.none": "沒有人",
+  "lists.replies_policy.title": "顯示回覆:",
   "lists.search": "搜尋您關注的使用者",
   "lists.subheading": "您的名單",
-  "load_pending": "{count, plural, other {# 個新項目}}",
+  "load_pending": "{count, plural, one {# 個新項目} other {# 個新項目}}",
   "loading_indicator.label": "讀取中...",
   "media_gallery.toggle_visible": "切換可見性",
   "missing_indicator.label": "找不到",
@@ -303,30 +303,30 @@
   "navigation_bar.preferences": "偏好設定",
   "navigation_bar.public_timeline": "聯邦時間軸",
   "navigation_bar.security": "安全性",
-  "notification.favourite": "{name} 把你的嘟文加入了最愛",
-  "notification.follow": "{name} 關注了你",
-  "notification.follow_request": "{name} 要求關注你",
-  "notification.mention": "{name} 提到了你",
+  "notification.favourite": "{name} 把您的嘟文加入了最愛",
+  "notification.follow": "{name} 關注了您",
+  "notification.follow_request": "{name} 要求關注您",
+  "notification.mention": "{name} 提到了您",
   "notification.own_poll": "您的投票已結束",
-  "notification.poll": "您投過的投票已經結束",
-  "notification.reblog": "{name}轉嘟了你的嘟文",
+  "notification.poll": "您曾投過的投票已經結束",
+  "notification.reblog": "{name} 轉嘟了您的嘟文",
   "notification.status": "{name} 剛剛嘟文",
   "notifications.clear": "清除通知",
-  "notifications.clear_confirmation": "確定要永久清除你的通知嗎?",
+  "notifications.clear_confirmation": "確定要永久清除您的通知嗎?",
   "notifications.column_settings.alert": "桌面通知",
   "notifications.column_settings.favourite": "最愛:",
   "notifications.column_settings.filter_bar.advanced": "顯示所有分類",
   "notifications.column_settings.filter_bar.category": "快速過濾欄",
   "notifications.column_settings.filter_bar.show": "顯示",
   "notifications.column_settings.follow": "新關注者:",
-  "notifications.column_settings.follow_request": "新的關注請求:",
+  "notifications.column_settings.follow_request": "新的關注請求:",
   "notifications.column_settings.mention": "提及:",
   "notifications.column_settings.poll": "投票結果:",
-  "notifications.column_settings.push": "推送通知",
+  "notifications.column_settings.push": "推播通知",
   "notifications.column_settings.reblog": "轉嘟:",
   "notifications.column_settings.show": "在欄位中顯示",
-  "notifications.column_settings.sound": "播放音效",
-  "notifications.column_settings.status": "新嘟文:",
+  "notifications.column_settings.sound": "播放聲音",
+  "notifications.column_settings.status": "新嘟文:",
   "notifications.filter.all": "全部",
   "notifications.filter.boosts": "轉嘟",
   "notifications.filter.favourites": "最愛",
@@ -334,12 +334,14 @@
   "notifications.filter.mentions": "提及",
   "notifications.filter.polls": "投票結果",
   "notifications.filter.statuses": "已跟隨使用者的最新動態",
+  "notifications.grant_permission": "授予權限",
   "notifications.group": "{count} 條通知",
   "notifications.mark_as_read": "將所有通知都標記為已讀",
   "notifications.permission_denied": "由於之前拒絕了瀏覽器請求,因此桌面通知不可用",
   "notifications.permission_denied_alert": "因為之前瀏覽器權限被拒絕,無法啟用桌面通知",
+  "notifications.permission_required": "因為尚未授予所需的權限,所以桌面通知不可用。",
   "notifications_permission_banner.enable": "啟用桌面通知",
-  "notifications_permission_banner.how_to_control": "啟用桌面通知以在 Mastodon 沒有開啟的時候接收通知。在已經啟用桌面通知的時候,你可以透過上面的 {icon} 按鈕準確的控制哪些類型的互動會產生桌面通知。",
+  "notifications_permission_banner.how_to_control": "啟用桌面通知以在 Mastodon 沒有開啟的時候接收通知。在已經啟用桌面通知的時候,您可以透過上面的 {icon} 按鈕準確的控制哪些類型的互動會產生桌面通知。",
   "notifications_permission_banner.title": "不要錯過任何東西!",
   "picture_in_picture.restore": "還原",
   "poll.closed": "已關閉",
@@ -347,37 +349,37 @@
   "poll.total_people": "{count, plural, one {# 個投票} other {# 個投票}}",
   "poll.total_votes": "{count, plural, one {# 個投票} other {# 個投票}}",
   "poll.vote": "投票",
-  "poll.voted": "你已對此問題投票",
+  "poll.voted": "您已對此問題投票",
   "poll_button.add_poll": "建立投票",
   "poll_button.remove_poll": "移除投票",
-  "privacy.change": "調整隱私狀態",
-  "privacy.direct.long": "只有被提到的使用者能看到",
+  "privacy.change": "調整嘟文隱私狀態",
+  "privacy.direct.long": "只有被提及的使用者能看到",
   "privacy.direct.short": "私訊",
-  "privacy.private.long": "只有關注你的使用者能看到",
+  "privacy.private.long": "只有關注您的使用者能看到",
   "privacy.private.short": "僅關注者",
-  "privacy.public.long": "嘟到公開時間軸",
+  "privacy.public.long": "公開,且顯示於公開時間軸",
   "privacy.public.short": "公開",
   "privacy.unlisted.long": "公開,但不會顯示在公開時間軸",
   "privacy.unlisted.short": "不公開",
   "refresh": "重新整理",
   "regeneration_indicator.label": "載入中…",
-  "regeneration_indicator.sublabel": "你的主頁時間軸正在準備中!",
+  "regeneration_indicator.sublabel": "您的主頁時間軸正在準備中!",
   "relative_time.days": "{number} 天",
-  "relative_time.hours": "{number} 小時",
+  "relative_time.hours": "{number}小時前",
   "relative_time.just_now": "剛剛",
-  "relative_time.minutes": "{number} 分",
+  "relative_time.minutes": "{number} 分前",
   "relative_time.seconds": "{number} 秒",
   "relative_time.today": "今天",
   "reply_indicator.cancel": "取消",
   "report.forward": "轉寄到 {target}",
-  "report.forward_hint": "這個帳戶屬於其他站點。要像該站點發送匿名的檢舉訊息嗎?",
-  "report.hint": "這項訊息會發送到您伺服器的管理員。你可以提供檢舉這個帳戶的理由:",
-  "report.placeholder": "更多訊息",
+  "report.forward_hint": "這個帳戶屬於其他伺服器。要像該伺服器發送匿名的檢舉訊息嗎?",
+  "report.hint": "這項訊息會發送到您伺服器的管理員。您可以提供檢舉這個帳戶的理由:",
+  "report.placeholder": "其他備註",
   "report.submit": "送出",
   "report.target": "檢舉 {target}",
   "search.placeholder": "搜尋",
   "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": "輸入簡單的文字,搜尋符合的使用者名稱,帳戶名稱與標籤",
@@ -392,32 +394,32 @@
   "status.block": "封鎖 @{name}",
   "status.bookmark": "書籤",
   "status.cancel_reblog_private": "取消轉嘟",
-  "status.cannot_reblog": "這篇嘟文無法被轉嘟",
+  "status.cannot_reblog": "這則嘟文無法被轉嘟",
   "status.copy": "複製嘟文連結",
   "status.delete": "刪除",
-  "status.detailed_status": "對話的詳細內容",
+  "status.detailed_status": "詳細的對話內容",
   "status.direct": "發送私訊給 @{name}",
-  "status.embed": "嵌入",
+  "status.embed": "內嵌",
   "status.favourite": "最愛",
   "status.filtered": "已過濾",
   "status.load_more": "載入更多",
   "status.media_hidden": "隱藏媒體內容",
-  "status.mention": "提到 @{name}",
+  "status.mention": "提及 @{name}",
   "status.more": "更多",
   "status.mute": "靜音 @{name}",
   "status.mute_conversation": "靜音對話",
-  "status.open": "展開嘟文",
+  "status.open": "展開此嘟文",
   "status.pin": "釘選到個人資料頁",
   "status.pinned": "釘選的嘟文",
   "status.read_more": "閱讀更多",
   "status.reblog": "轉嘟",
   "status.reblog_private": "轉嘟給原有關注者",
   "status.reblogged_by": "{name} 轉嘟了",
-  "status.reblogs.empty": "還沒有人轉嘟。如果有,會顯示在這裡。",
+  "status.reblogs.empty": "還沒有人轉嘟過這則嘟文。當有人轉嘟時,它將於此顯示。",
   "status.redraft": "刪除 & 編輯",
   "status.remove_bookmark": "移除書籤",
   "status.reply": "回覆",
-  "status.replyAll": "回覆所有人",
+  "status.replyAll": "回覆討論串",
   "status.report": "檢舉 @{name}",
   "status.sensitive_warning": "敏感內容",
   "status.share": "分享",
@@ -428,15 +430,15 @@
   "status.show_thread": "顯示討論串",
   "status.uncached_media_warning": "無法使用",
   "status.unmute_conversation": "解除此對話的靜音",
-  "status.unpin": "解除置頂",
+  "status.unpin": "從個人頁面解除釘選",
   "suggestions.dismiss": "關閉建議",
   "suggestions.header": "您可能對這些東西有興趣…",
-  "tabs_bar.federated_timeline": "其他站點",
-  "tabs_bar.home": "主頁",
-  "tabs_bar.local_timeline": "本站",
+  "tabs_bar.federated_timeline": "聯邦宇宙",
+  "tabs_bar.home": "首頁",
+  "tabs_bar.local_timeline": "本機",
   "tabs_bar.notifications": "通知",
   "tabs_bar.search": "搜尋",
-  "time_remaining.days": "剩餘{number, plural, one {# 天數} other {# 天數}}",
+  "time_remaining.days": "剩餘{number, plural, one {# 天} other {# 天}}",
   "time_remaining.hours": "剩餘{number, plural, one {# 小時} other {# 小時}}",
   "time_remaining.minutes": "剩餘{number, plural, one {# 分鐘} other {# 分鐘}}",
   "time_remaining.moments": "剩餘時間",
@@ -447,27 +449,27 @@
   "timeline_hint.resources.statuses": "更早的嘟文",
   "trends.counter_by_accounts": "{count, plural,one {{counter} 人}other {{counter} 人}}正在討論",
   "trends.trending_now": "目前趨勢",
-  "ui.beforeunload": "如果離開 Mastodon,你的草稿將會不見。",
+  "ui.beforeunload": "如果離開 Mastodon,您的草稿將會不見。",
   "units.short.billion": "{count}B",
   "units.short.million": "{count}M",
   "units.short.thousand": "{count}K",
   "upload_area.title": "拖放來上傳",
-  "upload_button.label": "上傳媒體檔案 (JPEG, PNG, GIF, WebM, MP4, MOV)",
+  "upload_button.label": "上傳圖像、影片、或音樂檔案",
   "upload_error.limit": "已達到檔案上傳限制。",
-  "upload_error.poll": "不允許在投票上傳檔案。",
-  "upload_form.audio_description": "簡單描述內容給聽障人士",
+  "upload_error.poll": "不允許在投票中上傳檔案。",
+  "upload_form.audio_description": "描述內容給聽障人士",
   "upload_form.description": "為視障人士增加文字說明",
   "upload_form.edit": "編輯",
   "upload_form.thumbnail": "更改預覽圖",
   "upload_form.undo": "刪除",
-  "upload_form.video_description": "簡單描述給聽障或視障人士",
+  "upload_form.video_description": "描述給聽障或視障人士",
   "upload_modal.analyzing_picture": "正在分析圖片…",
   "upload_modal.apply": "套用",
   "upload_modal.choose_image": "選擇圖片",
-  "upload_modal.description_placeholder": "A quick brown fox 跳過那隻懶狗",
-  "upload_modal.detect_text": "從圖片偵測文字",
+  "upload_modal.description_placeholder": "我能吞下玻璃而不傷身體",
+  "upload_modal.detect_text": "從圖片中偵測文字",
   "upload_modal.edit_media": "編輯媒體",
-  "upload_modal.hint": "點擊或拖曳圓圈以選擇預覽縮圖。",
+  "upload_modal.hint": "於預覽中點擊或拖曳圓圈以選擇將於所有縮圖中顯示的焦點。",
   "upload_modal.preparing_ocr": "準備 OCR 中……",
   "upload_modal.preview_label": "預覽 ({ratio})",
   "upload_progress.label": "上傳中...",
diff --git a/app/javascript/mastodon/reducers/notifications.js b/app/javascript/mastodon/reducers/notifications.js
index 46a9d5376..1d4874717 100644
--- a/app/javascript/mastodon/reducers/notifications.js
+++ b/app/javascript/mastodon/reducers/notifications.js
@@ -12,7 +12,6 @@ import {
   NOTIFICATIONS_MARK_AS_READ,
   NOTIFICATIONS_SET_BROWSER_SUPPORT,
   NOTIFICATIONS_SET_BROWSER_PERMISSION,
-  NOTIFICATIONS_DISMISS_BROWSER_PERMISSION,
 } from '../actions/notifications';
 import {
   ACCOUNT_BLOCK_SUCCESS,
@@ -251,8 +250,6 @@ export default function notifications(state = initialState, action) {
     return state.set('browserSupport', action.value);
   case NOTIFICATIONS_SET_BROWSER_PERMISSION:
     return state.set('browserPermission', action.value);
-  case NOTIFICATIONS_DISMISS_BROWSER_PERMISSION:
-    return state.set('browserPermission', 'denied');
   default:
     return state;
   }
diff --git a/app/javascript/mastodon/reducers/settings.js b/app/javascript/mastodon/reducers/settings.js
index 057fa353a..357ab352a 100644
--- a/app/javascript/mastodon/reducers/settings.js
+++ b/app/javascript/mastodon/reducers/settings.js
@@ -44,6 +44,8 @@ const initialState = ImmutableMap({
       advanced: false,
     }),
 
+    dismissPermissionBanner: false,
+
     shows: ImmutableMap({
       follow: true,
       follow_request: false,
diff --git a/app/javascript/mastodon/rtl.js b/app/javascript/mastodon/rtl.js
deleted file mode 100644
index 89bed6de8..000000000
--- a/app/javascript/mastodon/rtl.js
+++ /dev/null
@@ -1,32 +0,0 @@
-// U+0590  to U+05FF  - Hebrew
-// U+0600  to U+06FF  - Arabic
-// U+0700  to U+074F  - Syriac
-// U+0750  to U+077F  - Arabic Supplement
-// U+0780  to U+07BF  - Thaana
-// U+07C0  to U+07FF  - N'Ko
-// U+0800  to U+083F  - Samaritan
-// U+08A0  to U+08FF  - Arabic Extended-A
-// U+FB1D  to U+FB4F  - Hebrew presentation forms
-// U+FB50  to U+FDFF  - Arabic presentation forms A
-// U+FE70  to U+FEFF  - Arabic presentation forms B
-
-const rtlChars = /[\u0590-\u083F]|[\u08A0-\u08FF]|[\uFB1D-\uFDFF]|[\uFE70-\uFEFF]/mg;
-
-export function isRtl(text) {
-  if (text.length === 0) {
-    return false;
-  }
-
-  text = text.replace(/(?:^|[^\/\w])@([a-z0-9_]+(@[a-z0-9\.\-]+)?)/ig, '');
-  text = text.replace(/(?:^|[^\/\w])#([\S]+)/ig, '');
-  text = text.replace(/\s+/g, '');
-  text = text.replace(/(\w\S+\.\w{2,}\S*)/g, '');
-
-  const matches = text.match(rtlChars);
-
-  if (!matches) {
-    return false;
-  }
-
-  return matches.length / text.length > 0.3;
-};
diff --git a/app/javascript/styles/mailer.scss b/app/javascript/styles/mailer.scss
index e25a80c04..55ebd3091 100644
--- a/app/javascript/styles/mailer.scss
+++ b/app/javascript/styles/mailer.scss
@@ -58,6 +58,16 @@ td {
   vertical-align: top;
 }
 
+.auto-dir {
+  p {
+    unicode-bidi: plaintext;
+  }
+
+  a {
+    unicode-bidi: isolate;
+  }
+}
+
 .email-table,
 .content-section,
 .column,
@@ -96,7 +106,7 @@ body {
 .col-3,
 .col-4,
 .col-5,
-.col-6, {
+.col-6 {
   font-size: 0;
   display: inline-block;
   width: 100%;
diff --git a/app/javascript/styles/mastodon-light/diff.scss b/app/javascript/styles/mastodon-light/diff.scss
index 7f7eee6fe..18d1f7ad0 100644
--- a/app/javascript/styles/mastodon-light/diff.scss
+++ b/app/javascript/styles/mastodon-light/diff.scss
@@ -355,11 +355,45 @@ html {
 .error-modal,
 .onboarding-modal,
 .report-modal__comment .setting-text__wrapper,
-.report-modal__comment .setting-text {
+.report-modal__comment .setting-text,
+.announcements,
+.picture-in-picture__header,
+.picture-in-picture__footer,
+.reactions-bar__item {
   background: $white;
   border: 1px solid lighten($ui-base-color, 8%);
 }
 
+.reactions-bar__item {
+  &:hover,
+  &:focus,
+  &:active {
+    background-color: $ui-base-color;
+  }
+}
+
+.reactions-bar__item.active {
+  background-color: mix($white, $ui-highlight-color, 80%);
+  border-color: mix(lighten($ui-base-color, 8%), $ui-highlight-color, 80%);
+}
+
+.media-modal__overlay .picture-in-picture__footer {
+  border: 0;
+}
+
+.picture-in-picture__header {
+  border-bottom: 0;
+}
+
+.announcements,
+.picture-in-picture__footer {
+  border-top: 0;
+}
+
+.icon-with-badge__badge {
+  border-color: $white;
+}
+
 .report-modal__comment {
   border-right-color: lighten($ui-base-color, 8%);
 }
@@ -512,6 +546,12 @@ html {
   }
 }
 
+.picture-in-picture-placeholder {
+  background: $white;
+  border-color: lighten($ui-base-color, 8%);
+  color: lighten($ui-base-color, 8%);
+}
+
 .brand__tagline {
   color: $ui-secondary-color;
 }
diff --git a/app/javascript/styles/mastodon/about.scss b/app/javascript/styles/mastodon/about.scss
index 3be0aee49..d6bd9e3c6 100644
--- a/app/javascript/styles/mastodon/about.scss
+++ b/app/javascript/styles/mastodon/about.scss
@@ -732,6 +732,7 @@ $small-breakpoint: 960px;
 
       &__column {
         flex: 1 1 50%;
+        overflow-x: hidden;
       }
     }
 
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index e0c33fb85..6ff640f8d 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -831,6 +831,7 @@
   p {
     margin-bottom: 20px;
     white-space: pre-wrap;
+    unicode-bidi: plaintext;
 
     &:last-child {
       margin-bottom: 0;
@@ -840,6 +841,7 @@
   a {
     color: $secondary-text-color;
     text-decoration: none;
+    unicode-bidi: isolate;
 
     &:hover {
       text-decoration: underline;
@@ -3457,6 +3459,12 @@ a.status-card.compact:hover {
   }
 }
 
+.column-header__permission-btn {
+  display: inline;
+  font-weight: inherit;
+  text-decoration: underline;
+}
+
 .column-header__setting-arrows {
   float: right;
 
@@ -3855,7 +3863,7 @@ a.status-card.compact:hover {
 }
 
 .column-settings__row {
-  .text-btn {
+  .text-btn:not(.column-header__permission-btn) {
     margin-bottom: 15px;
   }
 }
diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss
index f7e03f028..5ace0d97c 100644
--- a/app/javascript/styles/mastodon/forms.scss
+++ b/app/javascript/styles/mastodon/forms.scss
@@ -371,11 +371,6 @@ code {
       box-shadow: none;
     }
 
-    &:focus:invalid:not(:placeholder-shown),
-    &:required:invalid:not(:placeholder-shown) {
-      border-color: lighten($error-red, 12%);
-    }
-
     &:required:valid {
       border-color: $valid-value-color;
     }
@@ -391,6 +386,16 @@ code {
     }
   }
 
+  input[type=text],
+  input[type=number],
+  input[type=email],
+  input[type=password] {
+    &:focus:invalid:not(:placeholder-shown),
+    &:required:invalid:not(:placeholder-shown) {
+      border-color: lighten($error-red, 12%);
+    }
+  }
+
   .input.field_with_errors {
     label {
       color: lighten($error-red, 12%);
diff --git a/app/javascript/styles/mastodon/rtl.scss b/app/javascript/styles/mastodon/rtl.scss
index fbf26e30b..8051e4edb 100644
--- a/app/javascript/styles/mastodon/rtl.scss
+++ b/app/javascript/styles/mastodon/rtl.scss
@@ -17,15 +17,38 @@ body.rtl {
     margin-right: 15px;
   }
 
-  .display-name {
+  .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);
   }
@@ -195,6 +218,7 @@ body.rtl {
     margin-right: 0;
   }
 
+  .picture-in-picture__header__account .display-name,
   .detailed-status__display-name .display-name {
     text-align: right;
   }
@@ -205,6 +229,21 @@ body.rtl {
     float: right;
   }
 
+  .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;
@@ -416,4 +455,9 @@ body.rtl {
     left: auto;
     right: 0;
   }
+
+  .picture-in-picture {
+    right: auto;
+    left: 20px;
+  }
 }
diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb
index d56d47a2d..b9d43d74d 100644
--- a/app/lib/activitypub/activity/create.rb
+++ b/app/lib/activitypub/activity/create.rb
@@ -228,6 +228,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
     emoji ||= CustomEmoji.new(domain: @account.domain, shortcode: shortcode, uri: uri)
     emoji.image_remote_url = image_url
     emoji.save
+  rescue Seahorse::Client::NetworkingError
+    nil
   end
 
   def process_attachments
@@ -250,6 +252,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
         media_attachment.save
       rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError
         RedownloadMediaWorker.perform_in(rand(30..600).seconds, media_attachment.id)
+      rescue Seahorse::Client::NetworkingError
+        nil
       end
     end
 
diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb
index ebd25b398..ddd975c5f 100644
--- a/app/lib/feed_manager.rb
+++ b/app/lib/feed_manager.rb
@@ -279,6 +279,36 @@ class FeedManager
     end
   end
 
+  # Completely clear multiple feeds at once
+  # @param [Symbol] type
+  # @param [Array<Integer>] ids
+  # @return [void]
+  def clean_feeds!(type, ids)
+    reblogged_id_sets = {}
+
+    redis.pipelined do
+      ids.each do |feed_id|
+        redis.del(key(type, feed_id))
+        reblog_key = key(type, feed_id, 'reblogs')
+        # We collect a future for this: we don't block while getting
+        # it, but we can iterate over it later.
+        reblogged_id_sets[feed_id] = redis.zrange(reblog_key, 0, -1)
+        redis.del(reblog_key)
+      end
+    end
+
+    # Remove all of the reblog tracking keys we just removed the
+    # references to.
+    redis.pipelined do
+      reblogged_id_sets.each do |feed_id, future|
+        future.value.each do |reblogged_id|
+          reblog_set_key = key(type, feed_id, "reblogs:#{reblogged_id}")
+          redis.del(reblog_set_key)
+        end
+      end
+    end
+  end
+
   private
 
   # Trim a feed to maximum size by removing older items
diff --git a/app/models/account.rb b/app/models/account.rb
index b70978d2b..f6aba74c6 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -67,6 +67,7 @@ class Account < ApplicationRecord
   include Paginable
   include AccountCounters
   include DomainNormalizable
+  include DomainMaterializable
   include AccountMerging
 
   MAX_DISPLAY_NAME_LENGTH = (ENV['MAX_DISPLAY_NAME_CHARS'] || 30).to_i
@@ -103,11 +104,11 @@ class Account < ApplicationRecord
   scope :sensitized, -> { where.not(sensitized_at: nil) }
   scope :without_suspended, -> { where(suspended_at: nil) }
   scope :without_silenced, -> { where(silenced_at: nil) }
+  scope :without_instance_actor, -> { where.not(id: -99) }
   scope :recent, -> { reorder(id: :desc) }
   scope :bots, -> { where(actor_type: %w(Application Service)) }
   scope :groups, -> { where(actor_type: 'Group') }
   scope :alphabetic, -> { order(domain: :asc, username: :asc) }
-  scope :by_domain_accounts, -> { group(:domain).select(:domain, 'COUNT(*) AS accounts_count').order('accounts_count desc') }
   scope :matches_username, ->(value) { where(arel_table[:username].matches("#{value}%")) }
   scope :matches_display_name, ->(value) { where(arel_table[:display_name].matches("#{value}%")) }
   scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) }
@@ -226,7 +227,7 @@ class Account < ApplicationRecord
   end
 
   def suspended?
-    suspended_at.present?
+    suspended_at.present? && !instance_actor?
   end
 
   def suspended_permanently?
@@ -440,10 +441,6 @@ class Account < ApplicationRecord
       super - %w(statuses_count following_count followers_count)
     end
 
-    def domains
-      reorder(nil).pluck(Arel.sql('distinct accounts.domain'))
-    end
-
     def inboxes
       urls = reorder(nil).where(protocol: :activitypub).group(:preferred_inbox_url).pluck(Arel.sql("coalesce(nullif(accounts.shared_inbox_url, ''), accounts.inbox_url) AS preferred_inbox_url"))
       DeliveryFailureTracker.without_unavailable(urls)
@@ -583,17 +580,6 @@ class Account < ApplicationRecord
   end
 
   def clean_feed_manager
-    reblog_key       = FeedManager.instance.key(:home, id, 'reblogs')
-    reblogged_id_set = Redis.current.zrange(reblog_key, 0, -1)
-
-    Redis.current.pipelined do
-      Redis.current.del(FeedManager.instance.key(:home, id))
-      Redis.current.del(reblog_key)
-
-      reblogged_id_set.each do |reblogged_id|
-        reblog_set_key = FeedManager.instance.key(:home, id, "reblogs:#{reblogged_id}")
-        Redis.current.del(reblog_set_key)
-      end
-    end
+    FeedManager.instance.clean_feeds!(:home, [id])
   end
 end
diff --git a/app/models/account_filter.rb b/app/models/account_filter.rb
index 7b6012e0f..2b001385f 100644
--- a/app/models/account_filter.rb
+++ b/app/models/account_filter.rb
@@ -45,7 +45,7 @@ class AccountFilter
   def scope_for(key, value)
     case key.to_s
     when 'local'
-      Account.local
+      Account.local.without_instance_actor
     when 'remote'
       Account.remote
     when 'by_domain'
diff --git a/app/models/concerns/domain_materializable.rb b/app/models/concerns/domain_materializable.rb
new file mode 100644
index 000000000..88337f8c0
--- /dev/null
+++ b/app/models/concerns/domain_materializable.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module DomainMaterializable
+  extend ActiveSupport::Concern
+
+  included do
+    after_create_commit :refresh_instances_view
+  end
+
+  def refresh_instances_view
+    Instance.refresh unless domain.nil? || Instance.where(domain: domain).exists?
+  end
+end
diff --git a/app/models/domain_allow.rb b/app/models/domain_allow.rb
index 5fe0e3a29..4b0a89c18 100644
--- a/app/models/domain_allow.rb
+++ b/app/models/domain_allow.rb
@@ -12,6 +12,7 @@
 
 class DomainAllow < ApplicationRecord
   include DomainNormalizable
+  include DomainMaterializable
 
   validates :domain, presence: true, uniqueness: true, domain: true
 
diff --git a/app/models/domain_block.rb b/app/models/domain_block.rb
index 2b18e01fa..bba04c603 100644
--- a/app/models/domain_block.rb
+++ b/app/models/domain_block.rb
@@ -12,10 +12,12 @@
 #  reject_reports  :boolean          default(FALSE), not null
 #  private_comment :text
 #  public_comment  :text
+#  obfuscate       :boolean          default(FALSE), not null
 #
 
 class DomainBlock < ApplicationRecord
   include DomainNormalizable
+  include DomainMaterializable
 
   enum severity: [:silence, :suspend, :noop]
 
@@ -72,4 +74,23 @@ class DomainBlock < ApplicationRecord
     scope = suspend? ? accounts.where(suspended_at: created_at) : accounts.where(silenced_at: created_at)
     scope.count
   end
+
+  def public_domain
+    return domain unless obfuscate?
+
+    length        = domain.size
+    visible_ratio = length / 4
+
+    domain.chars.map.with_index do |chr, i|
+      if i > visible_ratio && i < length - visible_ratio && chr != '.'
+        '*'
+      else
+        chr
+      end
+    end.join
+  end
+
+  def domain_digest
+    Digest::SHA256.hexdigest(domain)
+  end
 end
diff --git a/app/models/favourite.rb b/app/models/favourite.rb
index bf0ec4449..35028b7dd 100644
--- a/app/models/favourite.rb
+++ b/app/models/favourite.rb
@@ -36,7 +36,7 @@ class Favourite < ApplicationRecord
   end
 
   def decrement_cache_counters
-    return if association(:status).loaded? && (status.marked_for_destruction? || status.marked_for_mass_destruction?)
+    return if association(:status).loaded? && status.marked_for_destruction?
     status&.decrement_count!(:favourites_count)
   end
 end
diff --git a/app/models/follow.rb b/app/models/follow.rb
index 55a9da792..69a1722b3 100644
--- a/app/models/follow.rb
+++ b/app/models/follow.rb
@@ -26,7 +26,7 @@ class Follow < ApplicationRecord
   has_one :notification, as: :activity, dependent: :destroy
 
   validates :account_id, uniqueness: { scope: :target_account_id }
-  validates_with FollowLimitValidator, on: :create
+  validates_with FollowLimitValidator, on: :create, if: :rate_limit?
 
   scope :recent, -> { reorder(id: :desc) }
 
diff --git a/app/models/follow_request.rb b/app/models/follow_request.rb
index c1f19149b..2d2a77b59 100644
--- a/app/models/follow_request.rb
+++ b/app/models/follow_request.rb
@@ -26,7 +26,7 @@ class FollowRequest < ApplicationRecord
   has_one :notification, as: :activity, dependent: :destroy
 
   validates :account_id, uniqueness: { scope: :target_account_id }
-  validates_with FollowLimitValidator, on: :create
+  validates_with FollowLimitValidator, on: :create, if: :rate_limit?
 
   def authorize!
     account.follow!(target_account, reblogs: show_reblogs, notify: notify, uri: uri)
diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb
index fcec3e686..999d835e6 100644
--- a/app/models/form/admin_settings.rb
+++ b/app/models/form/admin_settings.rb
@@ -42,6 +42,7 @@ class Form::AdminSettings
     show_domain_blocks_rationale
     noindex
     outgoing_spoilers
+    require_invite_text
   ).freeze
 
   BOOLEAN_KEYS = %i(
@@ -62,6 +63,7 @@ class Form::AdminSettings
     trends
     trendable_by_default
     noindex
+    require_invite_text
   ).freeze
 
   UPLOAD_KEYS = %i(
diff --git a/app/models/import.rb b/app/models/import.rb
index 702453289..00a54892e 100644
--- a/app/models/import.rb
+++ b/app/models/import.rb
@@ -27,6 +27,7 @@ class Import < ApplicationRecord
   enum type: [:following, :blocking, :muting, :domain_blocking, :bookmarks]
 
   validates :type, presence: true
+  validates_with ImportValidator, on: :create
 
   has_attached_file :data
   validates_attachment_content_type :data, content_type: FILE_TYPES
diff --git a/app/models/instance.rb b/app/models/instance.rb
index 3c740f8a2..29be03662 100644
--- a/app/models/instance.rb
+++ b/app/models/instance.rb
@@ -1,26 +1,63 @@
 # frozen_string_literal: true
+# == Schema Information
+#
+# Table name: instances
+#
+#  domain         :string           primary key
+#  accounts_count :bigint(8)
+#
 
-class Instance
-  include ActiveModel::Model
+class Instance < ApplicationRecord
+  self.primary_key = :domain
 
-  attr_accessor :domain, :accounts_count, :domain_block
+  has_many :accounts, foreign_key: :domain, primary_key: :domain
 
-  def initialize(resource)
-    @domain         = resource.domain
-    @accounts_count = resource.respond_to?(:accounts_count) ? resource.accounts_count : nil
-    @domain_block   = resource.is_a?(DomainBlock) ? resource : DomainBlock.rule_for(domain)
-    @domain_allow   = resource.is_a?(DomainAllow) ? resource : DomainAllow.rule_for(domain)
+  belongs_to :domain_block, foreign_key: :domain, primary_key: :domain
+  belongs_to :domain_allow, foreign_key: :domain, primary_key: :domain
+
+  scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) }
+
+  def self.refresh
+    Scenic.database.refresh_materialized_view(table_name, concurrently: true, cascade: false)
   end
 
-  def countable?
-    @accounts_count.present?
+  def readonly?
+    true
   end
 
-  def to_param
-    domain
+  def delivery_failure_tracker
+    @delivery_failure_tracker ||= DeliveryFailureTracker.new(domain)
+  end
+
+  def following_count
+    @following_count ||= Follow.where(account: accounts).count
+  end
+
+  def followers_count
+    @followers_count ||= Follow.where(target_account: accounts).count
+  end
+
+  def reports_count
+    @reports_count ||= Report.where(target_account: accounts).count
   end
 
-  def cache_key
+  def blocks_count
+    @blocks_count ||= Block.where(target_account: accounts).count
+  end
+
+  def public_comment
+    domain_block&.public_comment
+  end
+
+  def private_comment
+    domain_block&.private_comment
+  end
+
+  def media_storage
+    @media_storage ||= MediaAttachment.where(account: accounts).sum(:file_file_size)
+  end
+
+  def to_param
     domain
   end
 end
diff --git a/app/models/instance_filter.rb b/app/models/instance_filter.rb
index 9c467bc27..0598d8fea 100644
--- a/app/models/instance_filter.rb
+++ b/app/models/instance_filter.rb
@@ -13,18 +13,27 @@ class InstanceFilter
   end
 
   def results
-    if params[:limited].present?
-      scope = DomainBlock
-      scope = scope.matches_domain(params[:by_domain]) if params[:by_domain].present?
-      scope.order(id: :desc)
-    elsif params[:allowed].present?
-      scope = DomainAllow
-      scope = scope.matches_domain(params[:by_domain]) if params[:by_domain].present?
-      scope.order(id: :desc)
+    scope = Instance.includes(:domain_block, :domain_allow).order(accounts_count: :desc)
+
+    params.each do |key, value|
+      scope.merge!(scope_for(key, value.to_s.strip)) if value.present?
+    end
+
+    scope
+  end
+
+  private
+
+  def scope_for(key, value)
+    case key.to_s
+    when 'limited'
+      Instance.joins(:domain_block).reorder(Arel.sql('domain_blocks.id desc'))
+    when 'allowed'
+      Instance.joins(:domain_allow).reorder(Arel.sql('domain_allows.id desc'))
+    when 'by_domain'
+      Instance.matches_domain(value)
     else
-      scope = Account.remote
-      scope = scope.matches_domain(params[:by_domain]) if params[:by_domain].present?
-      scope.by_domain_accounts
+      raise "Unknown filter: #{key}"
     end
   end
 end
diff --git a/app/models/list.rb b/app/models/list.rb
index 655d55ff6..cdc6ebdb3 100644
--- a/app/models/list.rb
+++ b/app/models/list.rb
@@ -34,17 +34,6 @@ class List < ApplicationRecord
   private
 
   def clean_feed_manager
-    reblog_key       = FeedManager.instance.key(:list, id, 'reblogs')
-    reblogged_id_set = Redis.current.zrange(reblog_key, 0, -1)
-
-    Redis.current.pipelined do
-      Redis.current.del(FeedManager.instance.key(:list, id))
-      Redis.current.del(reblog_key)
-
-      reblogged_id_set.each do |reblogged_id|
-        reblog_set_key = FeedManager.instance.key(:list, id, "reblogs:#{reblogged_id}")
-        Redis.current.del(reblog_set_key)
-      end
-    end
+    FeedManager.instance.clean_feeds!(:list, [id])
   end
 end
diff --git a/app/models/poll.rb b/app/models/poll.rb
index b5deafcc2..e1ca55252 100644
--- a/app/models/poll.rb
+++ b/app/models/poll.rb
@@ -25,7 +25,7 @@ class Poll < ApplicationRecord
   belongs_to :account
   belongs_to :status
 
-  has_many :votes, class_name: 'PollVote', inverse_of: :poll, dependent: :destroy
+  has_many :votes, class_name: 'PollVote', inverse_of: :poll, dependent: :delete_all
 
   has_many :notifications, as: :activity, dependent: :destroy
 
diff --git a/app/models/report.rb b/app/models/report.rb
index f31bcfd2e..cd08120e4 100644
--- a/app/models/report.rb
+++ b/app/models/report.rb
@@ -14,6 +14,7 @@
 #  target_account_id          :bigint(8)        not null
 #  assigned_account_id        :bigint(8)
 #  uri                        :string
+#  forwarded                  :boolean
 #
 
 class Report < ApplicationRecord
diff --git a/app/models/status.rb b/app/models/status.rb
index d1ac2e4f2..0d15304b6 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -234,14 +234,6 @@ class Status < ApplicationRecord
     @emojis = CustomEmoji.from_text(fields.join(' '), account.domain)
   end
 
-  def mark_for_mass_destruction!
-    @marked_for_mass_destruction = true
-  end
-
-  def marked_for_mass_destruction?
-    @marked_for_mass_destruction
-  end
-
   def replies_count
     status_stat&.replies_count || 0
   end
@@ -498,7 +490,7 @@ class Status < ApplicationRecord
   end
 
   def decrement_counter_caches
-    return if direct_visibility? || marked_for_mass_destruction?
+    return if direct_visibility?
 
     account&.decrement_count!(:statuses_count)
     reblog&.decrement_count!(:reblogs_count) if reblog?
@@ -508,7 +500,7 @@ class Status < ApplicationRecord
   def unlink_from_conversations
     return unless direct_visibility?
 
-    mentioned_accounts = mentions.includes(:account).map(&:account)
+    mentioned_accounts = (association(:mentions).loaded? ? mentions : mentions.includes(:account)).map(&:account)
     inbox_owners       = mentioned_accounts.select(&:local?) + (account.local? ? [account] : [])
 
     inbox_owners.each do |inbox_owner|
diff --git a/app/models/unavailable_domain.rb b/app/models/unavailable_domain.rb
index e2918b586..5e8870bde 100644
--- a/app/models/unavailable_domain.rb
+++ b/app/models/unavailable_domain.rb
@@ -12,6 +12,8 @@
 class UnavailableDomain < ApplicationRecord
   include DomainNormalizable
 
+  validates :domain, presence: true, uniqueness: true
+
   after_commit :reset_cache!
 
   private
diff --git a/app/models/user.rb b/app/models/user.rb
index 984f04b4e..9316eb228 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -82,7 +82,8 @@ class User < ApplicationRecord
   has_many :webauthn_credentials, dependent: :destroy
 
   has_one :invite_request, class_name: 'UserInviteRequest', inverse_of: :user, dependent: :destroy
-  accepts_nested_attributes_for :invite_request, reject_if: ->(attributes) { attributes['text'].blank? }
+  accepts_nested_attributes_for :invite_request, reject_if: ->(attributes) { attributes['text'].blank? && !Setting.require_invite_text }
+  validates :invite_request, presence: true, on: :create, if: :invite_text_required?
 
   validates :locale, inclusion: I18n.available_locales.map(&:to_s), if: :locale?
   validates_with BlacklistedEmailValidator, on: :create
@@ -127,7 +128,7 @@ class User < ApplicationRecord
            to: :settings, prefix: :setting, allow_nil: false
 
   attr_reader :invite_code, :sign_in_token_attempt
-  attr_writer :external
+  attr_writer :external, :bypass_invite_request_check
 
   def confirmed?
     confirmed_at.present?
@@ -428,6 +429,10 @@ class User < ApplicationRecord
     !!@external
   end
 
+  def bypass_invite_request_check?
+    @bypass_invite_request_check
+  end
+
   def sanitize_languages
     return if chosen_languages.nil?
     chosen_languages.reject!(&:blank?)
@@ -465,4 +470,8 @@ class User < ApplicationRecord
   def validate_email_dns?
     email_changed? && !(Rails.env.test? || Rails.env.development?)
   end
+
+  def invite_text_required?
+    Setting.require_invite_text && !invited? && !external? && !bypass_invite_request_check?
+  end
 end
diff --git a/app/policies/account_policy.rb b/app/policies/account_policy.rb
index 262ada42e..672e1786b 100644
--- a/app/policies/account_policy.rb
+++ b/app/policies/account_policy.rb
@@ -14,7 +14,7 @@ class AccountPolicy < ApplicationPolicy
   end
 
   def suspend?
-    staff? && !record.user&.staff?
+    staff? && !record.user&.staff? && !record.instance_actor?
   end
 
   def destroy?
@@ -62,6 +62,6 @@ class AccountPolicy < ApplicationPolicy
   end
 
   def memorialize?
-    admin? && !record.user&.admin?
+    admin? && !record.user&.admin? && !record.instance_actor?
   end
 end
diff --git a/app/policies/domain_block_policy.rb b/app/policies/domain_block_policy.rb
index 47c0a81af..543259cce 100644
--- a/app/policies/domain_block_policy.rb
+++ b/app/policies/domain_block_policy.rb
@@ -13,6 +13,10 @@ class DomainBlockPolicy < ApplicationPolicy
     admin?
   end
 
+  def update?
+    admin?
+  end
+
   def destroy?
     admin?
   end
diff --git a/app/presenters/instance_presenter.rb b/app/presenters/instance_presenter.rb
index ee559037f..a37d904dc 100644
--- a/app/presenters/instance_presenter.rb
+++ b/app/presenters/instance_presenter.rb
@@ -29,7 +29,7 @@ class InstancePresenter
   end
 
   def domain_count
-    Rails.cache.fetch('distinct_domain_count') { Account.distinct.count(:domain) }
+    Rails.cache.fetch('distinct_domain_count') { Instance.count }
   end
 
   def sample_accounts
diff --git a/app/serializers/manifest_serializer.rb b/app/serializers/manifest_serializer.rb
index 21ec0d4be..dafe8f55b 100644
--- a/app/serializers/manifest_serializer.rb
+++ b/app/serializers/manifest_serializer.rb
@@ -7,7 +7,7 @@ class ManifestSerializer < ActiveModel::Serializer
   attributes :name, :short_name, :description,
              :icons, :theme_color, :background_color,
              :display, :start_url, :scope,
-             :share_target
+             :share_target, :shortcuts
 
   def name
     object.site_title
@@ -64,4 +64,42 @@ class ManifestSerializer < ActiveModel::Serializer
       },
     }
   end
+
+  def shortcuts
+    [
+      {
+        name: 'New toot',
+        url: '/web/statuses/new',
+        icons: [
+          {
+            src: '/shortcuts/new-status.png',
+            type: 'image/png',
+            sizes: '192x192',
+          },
+        ],
+      },
+      {
+        name: 'Notifications',
+        url: '/web/notifications',
+        icons: [
+          {
+            src: '/shortcuts/notifications.png',
+            type: 'image/png',
+            sizes: '192x192',
+          },
+        ],
+      },
+      {
+        name: 'Direct messages',
+        url: '/web/timelines/direct',
+        icons: [
+          {
+            src: '/shortcuts/direct.png',
+            type: 'image/png',
+            sizes: '192x192',
+          },
+        ],
+      },
+    ]
+  end
 end
diff --git a/app/services/activitypub/fetch_remote_account_service.rb b/app/services/activitypub/fetch_remote_account_service.rb
index e5bd0c47c..9d01f5386 100644
--- a/app/services/activitypub/fetch_remote_account_service.rb
+++ b/app/services/activitypub/fetch_remote_account_service.rb
@@ -28,7 +28,7 @@ class ActivityPub::FetchRemoteAccountService < BaseService
 
     return unless only_key || verified_webfinger?
 
-    ActivityPub::ProcessAccountService.new.call(@username, @domain, @json, only_key: only_key)
+    ActivityPub::ProcessAccountService.new.call(@username, @domain, @json, only_key: only_key, verified_webfinger: !only_key)
   rescue Oj::ParseError
     nil
   end
diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb
index 0201c30b3..27b088240 100644
--- a/app/services/activitypub/process_account_service.rb
+++ b/app/services/activitypub/process_account_service.rb
@@ -28,6 +28,8 @@ class ActivityPub::ProcessAccountService < BaseService
         update_account
         process_tags
         process_attachments
+
+        process_duplicate_accounts! if @options[:verified_webfinger]
       else
         raise Mastodon::RaceConditionError
       end
@@ -70,34 +72,42 @@ class ActivityPub::ProcessAccountService < BaseService
     @account.protocol            = :activitypub
 
     set_suspension!
+    set_immediate_protocol_attributes!
+    set_fetchable_key! unless @account.suspended? && @account.suspension_origin_local?
     set_immediate_attributes! unless @account.suspended?
-    set_fetchable_attributes! unless @options[:only_keys] || @account.suspended?
+    set_fetchable_attributes! unless @options[:only_key] || @account.suspended?
 
     @account.save_with_optional_media!
   end
 
-  def set_immediate_attributes!
+  def set_immediate_protocol_attributes!
     @account.inbox_url               = @json['inbox'] || ''
     @account.outbox_url              = @json['outbox'] || ''
     @account.shared_inbox_url        = (@json['endpoints'].is_a?(Hash) ? @json['endpoints']['sharedInbox'] : @json['sharedInbox']) || ''
     @account.followers_url           = @json['followers'] || ''
-    @account.featured_collection_url = @json['featured'] || ''
-    @account.devices_url             = @json['devices'] || ''
     @account.url                     = url || @uri
     @account.uri                     = @uri
+    @account.actor_type              = actor_type
+  end
+
+  def set_immediate_attributes!
+    @account.featured_collection_url = @json['featured'] || ''
+    @account.devices_url             = @json['devices'] || ''
     @account.display_name            = @json['name'] || ''
     @account.note                    = @json['summary'] || ''
     @account.locked                  = @json['manuallyApprovesFollowers'] || false
     @account.fields                  = property_values || {}
     @account.also_known_as           = as_array(@json['alsoKnownAs'] || []).map { |item| value_or_id(item) }
-    @account.actor_type              = actor_type
     @account.discoverable            = @json['discoverable'] || false
   end
 
+  def set_fetchable_key!
+    @account.public_key        = public_key || ''
+  end
+
   def set_fetchable_attributes!
     @account.avatar_remote_url = image_url('icon')  || '' unless skip_download?
     @account.header_remote_url = image_url('image') || '' unless skip_download?
-    @account.public_key        = public_key || ''
     @account.statuses_count    = outbox_total_items    if outbox_total_items.present?
     @account.following_count   = following_total_items if following_total_items.present?
     @account.followers_count   = followers_total_items if followers_total_items.present?
@@ -142,6 +152,12 @@ class ActivityPub::ProcessAccountService < BaseService
     VerifyAccountLinksWorker.perform_async(@account.id)
   end
 
+  def process_duplicate_accounts!
+    return unless Account.where(uri: @account.uri).where.not(id: @account.id).exists?
+
+    AccountMergingWorker.perform_async(@account.id)
+  end
+
   def actor_type
     if @json['type'].is_a?(Array)
       @json['type'].find { |type| ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES.include?(type) }
diff --git a/app/services/batched_remove_status_service.rb b/app/services/batched_remove_status_service.rb
index 707672ee0..2b649ee22 100644
--- a/app/services/batched_remove_status_service.rb
+++ b/app/services/batched_remove_status_service.rb
@@ -3,32 +3,41 @@
 class BatchedRemoveStatusService < BaseService
   include Redisable
 
-  # Delete given statuses and reblogs of them
-  # Dispatch PuSH updates of the deleted statuses, but only local ones
-  # Dispatch Salmon deletes, unique per domain, of the deleted statuses, but only local ones
-  # Remove statuses from home feeds
-  # Push delete events to streaming API for home feeds and public feeds
-  # @param [Enumerable<Status>] statuses A preferably batched array of statuses
+  # Delete multiple statuses and reblogs of them as efficiently as possible
+  # @param [Enumerable<Status>] statuses An array of statuses
   # @param [Hash] options
-  # @option [Boolean] :skip_side_effects
+  # @option [Boolean] :skip_side_effects Do not modify feeds and send updates to streaming API
   def call(statuses, **options)
-    statuses = Status.where(id: statuses.map(&:id)).includes(:account).flat_map { |status| [status] + status.reblogs.includes(:account).to_a }
+    ActiveRecord::Associations::Preloader.new.preload(statuses, options[:skip_side_effects] ? :reblogs : [:account, :tags, reblogs: :account])
 
-    @mentions = statuses.each_with_object({}) { |s, h| h[s.id] = s.active_mentions.includes(:account).to_a }
-    @tags     = statuses.each_with_object({}) { |s, h| h[s.id] = s.tags.pluck(:name) }
+    statuses_and_reblogs = statuses.flat_map { |status| [status] + status.reblogs }
 
-    @json_payloads = statuses.each_with_object({}) { |s, h| h[s.id] = Oj.dump(event: :delete, payload: s.id.to_s) }
+    # The conversations for direct visibility statuses also need
+    # to be manually updated. This part is not efficient but we
+    # rely on direct visibility statuses being relatively rare.
+    statuses_with_account_conversations = statuses.select(&:direct_visibility?)
 
-    # Ensure that rendered XML reflects destroyed state
-    statuses.each do |status|
-      status.mark_for_mass_destruction!
-      status.destroy
+    ActiveRecord::Associations::Preloader.new.preload(statuses_with_account_conversations, [mentions: :account])
+
+    statuses_with_account_conversations.each do |status|
+      status.send(:unlink_from_conversations)
+      unpush_from_direct_timelines(status)
     end
 
+    # We do not batch all deletes into one to avoid having a long-running
+    # transaction lock the database, but we use the delete method instead
+    # of destroy to avoid all callbacks. We rely on foreign keys to
+    # cascade the delete faster without loading the associations.
+    statuses_and_reblogs.each_slice(50) { |slice| Status.where(id: slice.map(&:id)).delete_all }
+
+    # Since we skipped all callbacks, we also need to manually
+    # deindex the statuses
+    Chewy.strategy.current.update(StatusesIndex, statuses_and_reblogs) if Chewy.enabled?
+
     return if options[:skip_side_effects]
 
     # Batch by source account
-    statuses.group_by(&:account_id).each_value do |account_statuses|
+    statuses_and_reblogs.group_by(&:account_id).each_value do |account_statuses|
       account = account_statuses.first.account
 
       next unless account
@@ -38,20 +47,18 @@ class BatchedRemoveStatusService < BaseService
     end
 
     # Cannot be batched
-    statuses.each do |status|
-      unpush_from_public_timelines(status)
-      unpush_from_direct_timelines(status) if status.direct_visibility?
+    @status_id_cutoff = Mastodon::Snowflake.id_at(2.weeks.ago)
+    redis.pipelined do
+      statuses.each do |status|
+        unpush_from_public_timelines(status)
+      end
     end
   end
 
   private
 
   def unpush_from_home_timelines(account, statuses)
-    recipients = account.followers_for_local_distribution.to_a
-
-    recipients << account if account.local?
-
-    recipients.each do |follower|
+    account.followers_for_local_distribution.includes(:user).reorder(nil).find_each do |follower|
       statuses.each do |status|
         FeedManager.instance.unpush_from_home(follower, status)
       end
@@ -59,7 +66,7 @@ class BatchedRemoveStatusService < BaseService
   end
 
   def unpush_from_list_timelines(account, statuses)
-    account.lists_for_local_distribution.select(:id, :account_id).each do |list|
+    account.lists_for_local_distribution.select(:id, :account_id).includes(account: :user).reorder(nil).find_each do |list|
       statuses.each do |status|
         FeedManager.instance.unpush_from_list(list, status)
       end
@@ -67,40 +74,27 @@ class BatchedRemoveStatusService < BaseService
   end
 
   def unpush_from_public_timelines(status)
-    return unless status.public_visibility?
+    return unless status.public_visibility? && status.id > @status_id_cutoff
 
-    payload = @json_payloads[status.id]
+    payload = Oj.dump(event: :delete, payload: status.id.to_s)
 
-    redis.pipelined do
-      redis.publish('timeline:public', payload)
-      if status.local?
-        redis.publish('timeline:public:local', payload)
-      else
-        redis.publish('timeline:public:remote', payload)
-      end
-      if status.media_attachments.any?
-        redis.publish('timeline:public:media', payload)
-        if status.local?
-          redis.publish('timeline:public:local:media', payload)
-        else
-          redis.publish('timeline:public:remote:media', payload)
-        end
-      end
+    redis.publish('timeline:public', payload)
+    redis.publish(status.local? ? 'timeline:public:local' : 'timeline:public:remote', payload)
 
-      @tags[status.id].each do |hashtag|
-        redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}", payload)
-        redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}:local", payload) if status.local?
-      end
+    if status.media_attachments.any?
+      redis.publish('timeline:public:media', payload)
+      redis.publish(status.local? ? 'timeline:public:local:media' : 'timeline:public:remote:media', payload)
+    end
+
+    status.tags.map { |tag| tag.name.mb_chars.downcase }.each do |hashtag|
+      redis.publish("timeline:hashtag:#{hashtag}", payload)
+      redis.publish("timeline:hashtag:#{hashtag}:local", payload) if status.local?
     end
   end
 
   def unpush_from_direct_timelines(status)
-    payload = @json_payloads[status.id]
-    redis.pipelined do
-      @mentions[status.id].each do |mention|
-        FeedManager.instance.unpush_from_direct(mention.account, status) if mention.account.local?
-      end
-      FeedManager.instance.unpush_from_direct(status.account, status) if status.account.local?
+    status.mentions.each do |mention|
+      FeedManager.instance.unpush_from_direct(mention.account, status) if mention.account.local?
     end
   end
 end
diff --git a/app/services/delete_account_service.rb b/app/services/delete_account_service.rb
index 9cb80c95a..2bb533cfb 100644
--- a/app/services/delete_account_service.rb
+++ b/app/services/delete_account_service.rb
@@ -6,15 +6,19 @@ class DeleteAccountService < BaseService
   ASSOCIATIONS_ON_SUSPEND = %w(
     account_pins
     active_relationships
+    aliases
     block_relationships
     blocked_by_relationships
     conversation_mutes
     conversations
     custom_filters
+    devices
     domain_blocks
-    favourites
+    featured_tags
     follow_requests
+    identity_proofs
     list_accounts
+    migrations
     mute_relationships
     muted_by_relationships
     notifications
@@ -25,6 +29,31 @@ class DeleteAccountService < BaseService
     status_pins
   ).freeze
 
+  # The following associations have no important side-effects
+  # in callbacks and all of their own associations are secured
+  # by foreign keys, making them safe to delete without loading
+  # into memory
+  ASSOCIATIONS_WITHOUT_SIDE_EFFECTS = %w(
+    account_pins
+    aliases
+    conversation_mutes
+    conversations
+    custom_filters
+    devices
+    domain_blocks
+    featured_tags
+    follow_requests
+    identity_proofs
+    list_accounts
+    migrations
+    mute_relationships
+    muted_by_relationships
+    notifications
+    owned_lists
+    scheduled_statuses
+    status_pins
+  )
+
   ASSOCIATIONS_ON_DESTROY = %w(
     reports
     targeted_moderation_notes
@@ -55,19 +84,25 @@ class DeleteAccountService < BaseService
 
     @options[:skip_activitypub] = true if @options[:skip_side_effects]
 
-    reject_follows!
-    undo_follows!
-    purge_user!
-    purge_profile!
+    distribute_activities!
     purge_content!
     fulfill_deletion_request!
   end
 
   private
 
-  def reject_follows!
-    return if @account.local? || !@account.activitypub? || @options[:skip_activitypub]
+  def distribute_activities!
+    return if skip_activitypub?
+
+    if @account.local?
+      delete_actor!
+    elsif @account.activitypub?
+      reject_follows!
+      undo_follows!
+    end
+  end
 
+  def reject_follows!
     # When deleting a remote account, the account obviously doesn't
     # actually become deleted on its origin server, i.e. unlike a
     # locally deleted account it continues to have access to its home
@@ -81,8 +116,6 @@ class DeleteAccountService < BaseService
   end
 
   def undo_follows!
-    return if @account.local? || !@account.activitypub? || @options[:skip_activitypub]
-
     # When deleting a remote account, the account obviously doesn't
     # actually become deleted on its origin server, but following relationships
     # are severed on our end. Therefore, make the remote server aware that the
@@ -97,7 +130,7 @@ class DeleteAccountService < BaseService
   def purge_user!
     return if !@account.local? || @account.user.nil?
 
-    if @options[:reserve_email]
+    if keep_user_record?
       @account.user.disable!
       @account.user.invites.where(uses: 0).destroy_all
     else
@@ -106,30 +139,74 @@ class DeleteAccountService < BaseService
   end
 
   def purge_content!
-    distribute_delete_actor! if @account.local? && !@options[:skip_side_effects]
+    purge_user!
+    purge_profile!
+    purge_statuses!
+    purge_media_attachments!
+    purge_polls!
+    purge_generated_notifications!
+    purge_favourites!
+    purge_bookmarks!
+    purge_feeds!
+    purge_other_associations!
+
+    @account.destroy unless keep_account_record?
+  end
 
-    @account.statuses.reorder(nil).find_in_batches do |statuses|
-      statuses.reject! { |status| reported_status_ids.include?(status.id) } if @options[:reserve_username]
-      BatchedRemoveStatusService.new.call(statuses, skip_side_effects: @options[:skip_side_effects])
+  def purge_statuses!
+    @account.statuses.reorder(nil).where.not(id: reported_status_ids).in_batches do |statuses|
+      BatchedRemoveStatusService.new.call(statuses, skip_side_effects: skip_side_effects?)
     end
+  end
 
+  def purge_media_attachments!
     @account.media_attachments.reorder(nil).find_each do |media_attachment|
-      next if @options[:reserve_username] && reported_status_ids.include?(media_attachment.status_id)
+      next if keep_account_record? && reported_status_ids.include?(media_attachment.status_id)
 
       media_attachment.destroy
     end
+  end
+
+  def purge_polls!
+    @account.polls.reorder(nil).where.not(status_id: reported_status_ids).in_batches.delete_all
+  end
+
+  def purge_generated_notifications!
+    # By deleting polls and statuses without callbacks, we've left behind
+    # polymorphically associated notifications generated by this account
 
-    @account.polls.reorder(nil).find_each do |poll|
-      next if @options[:reserve_username] && reported_status_ids.include?(poll.status_id)
+    Notification.where(from_account: @account).in_batches.delete_all
+  end
+
+  def purge_favourites!
+    @account.favourites.in_batches do |favourites|
+      ids = favourites.pluck(:status_id)
+      StatusStat.where(status_id: ids).update_all('favourites_count = GREATEST(0, favourites_count - 1)')
+      Chewy.strategy.current.update(StatusesIndex, ids) if Chewy.enabled?
+      # Rails.cache.delete_multi would be better, but we don't have it yet
+      ids.each { |id| Rails.cache.delete("statuses/#{id}") }
+      favourites.delete_all
+    end
+  end
 
-      poll.destroy
+  def purge_bookmarks!
+    @account.bookmarks.in_batches do |bookmarks|
+      Chewy.strategy.current.update(StatusesIndex, bookmarks.pluck(:status_id)) if Chewy.enabled?
+      bookmarks.delete_all
     end
+  end
 
+  def purge_other_associations!
     associations_for_destruction.each do |association_name|
-      destroy_all(@account.public_send(association_name))
+      purge_association(association_name)
     end
+  end
 
-    @account.destroy unless @options[:reserve_username]
+  def purge_feeds!
+    return unless @account.local?
+
+    FeedManager.instance.clean_feeds!(:home, [@account.id])
+    FeedManager.instance.clean_feeds!(:list, @account.owned_lists.pluck(:id))
   end
 
   def purge_profile!
@@ -137,7 +214,7 @@ class DeleteAccountService < BaseService
     # there is no point wasting time updating
     # its values first
 
-    return unless @options[:reserve_username]
+    return unless keep_account_record?
 
     @account.silenced_at       = nil
     @account.suspended_at      = @options[:suspended_at] || Time.now.utc
@@ -152,6 +229,7 @@ class DeleteAccountService < BaseService
     @account.followers_count   = 0
     @account.following_count   = 0
     @account.moved_to_account  = nil
+    @account.also_known_as     = []
     @account.trust_level       = :untrusted
     @account.avatar.destroy
     @account.header.destroy
@@ -162,11 +240,17 @@ class DeleteAccountService < BaseService
     @account.deletion_request&.destroy
   end
 
-  def destroy_all(association)
-    association.in_batches.destroy_all
+  def purge_association(association_name)
+    association = @account.public_send(association_name)
+
+    if ASSOCIATIONS_WITHOUT_SIDE_EFFECTS.include?(association_name)
+      association.in_batches.delete_all
+    else
+      association.in_batches.destroy_all
+    end
   end
 
-  def distribute_delete_actor!
+  def delete_actor!
     ActivityPub::DeliveryWorker.push_bulk(delivery_inboxes) do |inbox_url|
       [delete_actor_json, @account.id, inbox_url]
     end
@@ -193,10 +277,26 @@ class DeleteAccountService < BaseService
   end
 
   def associations_for_destruction
-    if @options[:reserve_username]
+    if keep_account_record?
       ASSOCIATIONS_ON_SUSPEND
     else
       ASSOCIATIONS_ON_SUSPEND + ASSOCIATIONS_ON_DESTROY
     end
   end
+
+  def keep_user_record?
+    @options[:reserve_email]
+  end
+
+  def keep_account_record?
+    @options[:reserve_username]
+  end
+
+  def skip_side_effects?
+    @options[:skip_side_effects]
+  end
+
+  def skip_activitypub?
+    @options[:skip_activitypub]
+  end
 end
diff --git a/app/services/import_service.rb b/app/services/import_service.rb
index 288e47f1e..0c6ef2238 100644
--- a/app/services/import_service.rb
+++ b/app/services/import_service.rb
@@ -27,7 +27,7 @@ class ImportService < BaseService
 
   def import_follows!
     parse_import_data!(['Account address'])
-    import_relationships!('follow', 'unfollow', @account.following, follow_limit, reblogs: { header: 'Show boosts', default: true })
+    import_relationships!('follow', 'unfollow', @account.following, ROWS_PROCESSING_LIMIT, reblogs: { header: 'Show boosts', default: true })
   end
 
   def import_blocks!
@@ -85,6 +85,7 @@ class ImportService < BaseService
 
     head_items = items.uniq { |acct, _| acct.split('@')[1] }
     tail_items = items - head_items
+
     Import::RelationshipWorker.push_bulk(head_items + tail_items) do |acct, extra|
       [@account.id, acct, action, extra]
     end
@@ -133,10 +134,6 @@ class ImportService < BaseService
     Paperclip.io_adapters.for(@import.data).read
   end
 
-  def follow_limit
-    FollowLimitValidator.limit_for_account(@account)
-  end
-
   def relations_map_for_account(account, account_ids)
     {
       blocking: {},
diff --git a/app/services/remove_status_service.rb b/app/services/remove_status_service.rb
index 2918c6f6f..764ed288d 100644
--- a/app/services/remove_status_service.rb
+++ b/app/services/remove_status_service.rb
@@ -160,7 +160,7 @@ class RemoveStatusService < BaseService
   end
 
   def remove_from_direct
-    @mentions.each do |mention|
+    @status.active_mentions.each do |mention|
       FeedManager.instance.unpush_from_direct(mention.account, @status) if mention.account.local?
     end
   end
diff --git a/app/services/report_service.rb b/app/services/report_service.rb
index 1e955c1e7..9d9c7d6c9 100644
--- a/app/services/report_service.rb
+++ b/app/services/report_service.rb
@@ -24,7 +24,8 @@ class ReportService < BaseService
       target_account: @target_account,
       status_ids: @status_ids,
       comment: @comment,
-      uri: @options[:uri]
+      uri: @options[:uri],
+      forwarded: ActiveModel::Type::Boolean.new.cast(@options[:forward])
     )
   end
 
diff --git a/app/services/resolve_account_service.rb b/app/services/resolve_account_service.rb
index 74b0b82d0..3301aaf51 100644
--- a/app/services/resolve_account_service.rb
+++ b/app/services/resolve_account_service.rb
@@ -49,7 +49,7 @@ class ResolveAccountService < BaseService
     # Now it is certain, it is definitely a remote account, and it
     # either needs to be created, or updated from fresh data
 
-    process_account!
+    fetch_account!
   rescue Webfinger::Error, Oj::ParseError => e
     Rails.logger.debug "Webfinger query for #{@uri} failed: #{e}"
     nil
@@ -104,16 +104,12 @@ class ResolveAccountService < BaseService
     acct.gsub(/\Aacct:/, '').split('@')
   end
 
-  def process_account!
+  def fetch_account!
     return unless activitypub_ready?
 
     RedisLock.acquire(lock_options) do |lock|
       if lock.acquired?
-        @account = Account.find_remote(@username, @domain)
-
-        next if actor_json.nil?
-
-        @account = ActivityPub::ProcessAccountService.new.call(@username, @domain, actor_json)
+        @account = ActivityPub::FetchRemoteAccountService.new.call(actor_url)
       else
         raise Mastodon::RaceConditionError
       end
@@ -136,13 +132,6 @@ class ResolveAccountService < BaseService
     @actor_url ||= @webfinger.link('self', 'href')
   end
 
-  def actor_json
-    return @actor_json if defined?(@actor_json)
-
-    json        = fetch_resource(actor_url, false)
-    @actor_json = supported_context?(json) && equals_or_includes_any?(json['type'], ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES) ? json : nil
-  end
-
   def gone_from_origin?
     @gone
   end
diff --git a/app/services/resolve_url_service.rb b/app/services/resolve_url_service.rb
index 2b10ac1e0..5981e4d98 100644
--- a/app/services/resolve_url_service.rb
+++ b/app/services/resolve_url_service.rb
@@ -34,16 +34,16 @@ class ResolveURLService < BaseService
 
     # It may happen that the resource is a private toot, and thus not fetchable,
     # but we can return the toot if we already know about it.
-    uris = [@url]
+    scope = Status.where(uri: @url)
 
     # We don't have an index on `url`, so try guessing the `uri` from `url`
     parsed_url = Addressable::URI.parse(@url)
     parsed_url.path.match(%r{/@(?<username>#{Account::USERNAME_RE})/(?<status_id>[0-9]+)\Z}) do |matched|
       parsed_url.path = "/users/#{matched[:username]}/statuses/#{matched[:status_id]}"
-      uris << parsed_url.to_s
+      scope = scope.or(Status.where(uri: parsed_url.to_s, url: @url))
     end
 
-    status = Status.find_by(uri: uris)
+    status = scope.first
 
     authorize_with @on_behalf_of, status, :show? unless status.nil?
     status
diff --git a/app/validators/import_validator.rb b/app/validators/import_validator.rb
new file mode 100644
index 000000000..a182abfa5
--- /dev/null
+++ b/app/validators/import_validator.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+class ImportValidator < ActiveModel::Validator
+  KNOWN_HEADERS = [
+    'Account address',
+    '#domain',
+    '#uri',
+  ].freeze
+
+  def validate(import)
+    return if import.type.blank? || import.data.blank?
+
+    # We parse because newlines could be part of individual rows. This
+    # runs on create so we should be reading the local file here before
+    # it is uploaded to object storage or moved anywhere...
+    csv_data = CSV.parse(import.data.queued_for_write[:original].read)
+
+    row_count  = csv_data.size
+    row_count -= 1 if KNOWN_HEADERS.include?(csv_data.first&.first)
+
+    import.errors.add(:data, I18n.t('imports.errors.over_rows_processing_limit', count: ImportService::ROWS_PROCESSING_LIMIT)) if row_count > ImportService::ROWS_PROCESSING_LIMIT
+
+    case import.type
+    when 'following'
+      validate_following_import(import, row_count)
+    end
+  end
+
+  private
+
+  def validate_following_import(import, row_count)
+    base_limit = FollowLimitValidator.limit_for_account(import.account)
+
+    limit = begin
+      if import.overwrite?
+        base_limit
+      else
+        base_limit - import.account.following_count
+      end
+    end
+
+    import.errors.add(:data, I18n.t('users.follow_limit_reached', limit: base_limit)) if row_count > limit
+  end
+end
diff --git a/app/views/about/_domain_blocks.html.haml b/app/views/about/_domain_blocks.html.haml
index e0c5df41d..35a30f16e 100644
--- a/app/views/about/_domain_blocks.html.haml
+++ b/app/views/about/_domain_blocks.html.haml
@@ -7,6 +7,6 @@
     - domain_blocks.each do |domain_block|
       %tr
         %td.nowrap
-          %span{ title: domain_block.domain }= domain_block.domain
+          %span{ title: "SHA-256: #{domain_block.domain_digest}" }= domain_block.public_domain
         %td
           = domain_block.public_comment if display_blocks_rationale?
diff --git a/app/views/about/_registration.html.haml b/app/views/about/_registration.html.haml
index 6160ca4d4..e4d614d71 100644
--- a/app/views/about/_registration.html.haml
+++ b/app/views/about/_registration.html.haml
@@ -16,7 +16,7 @@
     - if approved_registrations?
       .fields-group
         = f.simple_fields_for :invite_request do |invite_request_fields|
-          = invite_request_fields.input :text, as: :text, wrapper: :with_block_label, required: false
+          = invite_request_fields.input :text, as: :text, wrapper: :with_block_label, required: Setting.require_invite_text
 
     .fields-group
       = f.input :agreement, as: :boolean, wrapper: :with_label, label: t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path), required: true, disabled: closed_registrations?
diff --git a/app/views/about/more.html.haml b/app/views/about/more.html.haml
index 0a12ab8d6..78f54ec5a 100644
--- a/app/views/about/more.html.haml
+++ b/app/views/about/more.html.haml
@@ -16,11 +16,11 @@
         .row__information-board
           .information-board__section
             %span= t 'about.user_count_before'
-            %strong= number_with_delimiter @instance_presenter.user_count
+            %strong= number_to_human @instance_presenter.user_count, strip_insignificant_zeros: true
             %span= t 'about.user_count_after', count: @instance_presenter.user_count
           .information-board__section
             %span= t 'about.status_count_before'
-            %strong= number_with_delimiter @instance_presenter.status_count
+            %strong= number_to_human @instance_presenter.status_count, strip_insignificant_zeros: true
             %span= t 'about.status_count_after', count: @instance_presenter.status_count
         .row__mascot
           .landing-page__mascot
diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml
index d5978eddd..27e1f80a7 100644
--- a/app/views/admin/accounts/show.html.haml
+++ b/app/views/admin/accounts/show.html.haml
@@ -1,6 +1,10 @@
 - content_for :page_title do
   = @account.acct
 
+- if @account.instance_actor?
+  .flash-message.notice
+    %strong= t('accounts.instance_actor_flash')
+
 = render 'application/card', account: @account
 
 - account = @account
@@ -242,3 +246,13 @@
 
     .actions
       = f.button :button, t('admin.account_moderation_notes.create'), type: :submit
+
+  %hr.spacer/
+
+  - if @account.user&.invite_request&.text&.present?
+    %div.speech-bubble
+      %div.speech-bubble__bubble
+        = @account.user&.invite_request&.text
+      %div.speech-bubble__owner
+        = admin_account_link_to @account
+        = t('admin.accounts.invite_request_text')
diff --git a/app/views/admin/domain_blocks/edit.html.haml b/app/views/admin/domain_blocks/edit.html.haml
index 8669bb6d1..c76a7a7f8 100644
--- a/app/views/admin/domain_blocks/edit.html.haml
+++ b/app/views/admin/domain_blocks/edit.html.haml
@@ -17,6 +17,9 @@
   .fields-group
     = f.input :reject_reports, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_reports'), hint: I18n.t('admin.domain_blocks.reject_reports_hint')
 
+  .fields-group
+    = f.input :obfuscate, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.obfuscate'), hint: I18n.t('admin.domain_blocks.obfuscate_hint')
+
   .field-group
     = f.input :private_comment, wrapper: :with_label, label: I18n.t('admin.domain_blocks.private_comment'), hint: t('admin.domain_blocks.private_comment_hint'), rows: 6
 
diff --git a/app/views/admin/domain_blocks/new.html.haml b/app/views/admin/domain_blocks/new.html.haml
index a643825df..b04ce7d9c 100644
--- a/app/views/admin/domain_blocks/new.html.haml
+++ b/app/views/admin/domain_blocks/new.html.haml
@@ -17,6 +17,9 @@
   .fields-group
     = f.input :reject_reports, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_reports'), hint: I18n.t('admin.domain_blocks.reject_reports_hint')
 
+  .fields-group
+    = f.input :obfuscate, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.obfuscate'), hint: I18n.t('admin.domain_blocks.obfuscate_hint')
+
   .field-group
     = f.input :private_comment, wrapper: :with_label, label: I18n.t('admin.domain_blocks.private_comment'), hint: t('admin.domain_blocks.private_comment_hint'), rows: 6
 
diff --git a/app/views/admin/instances/_instance.html.haml b/app/views/admin/instances/_instance.html.haml
new file mode 100644
index 000000000..188d0d984
--- /dev/null
+++ b/app/views/admin/instances/_instance.html.haml
@@ -0,0 +1,25 @@
+.directory__tag
+  = link_to admin_instance_path(instance) do
+    %h4
+      = instance.domain
+      %small
+        - if instance.domain_block
+          - first_item = true
+          - if !instance.domain_block.noop?
+            = t("admin.domain_blocks.severity.#{instance.domain_block.severity}")
+            - first_item = false
+          - unless instance.domain_block.suspend?
+            - if instance.domain_block.reject_media?
+              - unless first_item
+                &bull;
+              = t('admin.domain_blocks.rejecting_media')
+              - first_item = false
+            - if instance.domain_block.reject_reports?
+              - unless first_item
+                &bull;
+              = t('admin.domain_blocks.rejecting_reports')
+        - elsif whitelist_mode?
+          = t('admin.accounts.whitelisted')
+        - else
+          = t('admin.accounts.no_limits_imposed')
+    .trends__item__current{ title: t('admin.instances.known_accounts', count: instance.accounts_count) }= number_to_human instance.accounts_count, strip_insignificant_zeros: true
diff --git a/app/views/admin/instances/index.html.haml b/app/views/admin/instances/index.html.haml
index 696ba3c7f..5f20e7ec0 100644
--- a/app/views/admin/instances/index.html.haml
+++ b/app/views/admin/instances/index.html.haml
@@ -32,32 +32,10 @@
 
 %hr.spacer/
 
-- @instances.each do |instance|
-  .directory__tag
-    = link_to admin_instance_path(instance) do
-      %h4
-        = instance.domain
-        %small
-          - if instance.domain_block
-            - first_item = true
-            - if !instance.domain_block.noop?
-              = t("admin.domain_blocks.severity.#{instance.domain_block.severity}")
-              - first_item = false
-            - unless instance.domain_block.suspend?
-              - if instance.domain_block.reject_media?
-                - unless first_item
-                  &bull;
-                = t('admin.domain_blocks.rejecting_media')
-                - first_item = false
-              - if instance.domain_block.reject_reports?
-                - unless first_item
-                  &bull;
-                = t('admin.domain_blocks.rejecting_reports')
-          - elsif whitelist_mode?
-            = t('admin.accounts.whitelisted')
-          - else
-            = t('admin.accounts.no_limits_imposed')
-      - if instance.countable?
-        .trends__item__current{ title: t('admin.instances.known_accounts', count: instance.accounts_count) }= number_to_human instance.accounts_count, strip_insignificant_zeros: true
-
-= paginate paginated_instances
+- if @instances.empty?
+  %div.muted-hint.center-text
+    = t 'admin.instances.empty'
+- else
+  = render @instances
+
+= paginate @instances
diff --git a/app/views/admin/instances/show.html.haml b/app/views/admin/instances/show.html.haml
index 92e14c0df..0b9382771 100644
--- a/app/views/admin/instances/show.html.haml
+++ b/app/views/admin/instances/show.html.haml
@@ -3,57 +3,59 @@
 
 .dashboard__counters
   %div
+    = link_to admin_accounts_path(remote: '1', by_domain: @instance.domain) do
+      .dashboard__counters__num= number_with_delimiter @instance.accounts_count
+      .dashboard__counters__label= t 'admin.accounts.title'
+  %div
+    = link_to admin_reports_path(by_target_domain: @instance.domain) do
+      .dashboard__counters__num= number_with_delimiter @instance.reports_count
+      .dashboard__counters__label= t 'admin.instances.total_reported'
+  %div
     %div
-      .dashboard__counters__num= number_with_delimiter @following_count
-      .dashboard__counters__label= t 'admin.instances.total_followed_by_them'
+      .dashboard__counters__num= number_to_human_size @instance.media_storage
+      .dashboard__counters__label= t 'admin.instances.total_storage'
   %div
     %div
-      .dashboard__counters__num= number_with_delimiter @followers_count
-      .dashboard__counters__label= t 'admin.instances.total_followed_by_us'
+      .dashboard__counters__num= number_with_delimiter @instance.following_count
+      .dashboard__counters__label= t 'admin.instances.total_followed_by_them'
   %div
     %div
-      .dashboard__counters__num= number_to_human_size @media_storage
-      .dashboard__counters__label= t 'admin.instances.total_storage'
+      .dashboard__counters__num= number_with_delimiter @instance.followers_count
+      .dashboard__counters__label= t 'admin.instances.total_followed_by_us'
   %div
     %div
-      .dashboard__counters__num= number_with_delimiter @blocks_count
+      .dashboard__counters__num= number_with_delimiter @instance.blocks_count
       .dashboard__counters__label= t 'admin.instances.total_blocked_by_us'
-  %div
-    = link_to admin_reports_path(by_target_domain: @instance.domain) do
-      .dashboard__counters__num= number_with_delimiter @reports_count
-      .dashboard__counters__label= t 'admin.instances.total_reported'
+
   %div
     %div
       .dashboard__counters__num
-        - if @available
+        - if @instance.delivery_failure_tracker.available?
           = fa_icon 'check'
         - else
           = fa_icon 'times'
       .dashboard__counters__label= t 'admin.instances.delivery_available'
 
-- if @private_comment.present?
+- if @instance.private_comment.present?
   .speech-bubble
     .speech-bubble__bubble
-      = simple_format(h(@private_comment))
+      = simple_format(h(@instance.private_comment))
     .speech-bubble__owner= t 'admin.instances.private_comment'
 
-- if @public_comment.present?
+- if @instance.public_comment.present?
   .speech-bubble
     .speech-bubble__bubble
-      = simple_format(h(@public_comment))
+      = simple_format(h(@instance.public_comment))
     .speech-bubble__owner= t 'admin.instances.public_comment'
 
 %hr.spacer/
 
 %div.action-buttons
   %div
-    = link_to t('admin.accounts.title'), admin_accounts_path(remote: '1', by_domain: @instance.domain), class: 'button'
-
-  %div
-    - if @domain_allow
-      = link_to t('admin.domain_allows.undo'), admin_domain_allow_path(@domain_allow), class: 'button button--destructive', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete }
-    - elsif @domain_block
-      = link_to t('admin.domain_blocks.edit'), edit_admin_domain_block_path(@domain_block), class: 'button'
-      = link_to t('admin.domain_blocks.undo'), admin_domain_block_path(@domain_block), class: 'button'
+    - if @instance.domain_allow
+      = link_to t('admin.domain_allows.undo'), admin_domain_allow_path(@instance.domain_allow), class: 'button button--destructive', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete }
+    - elsif @instance.domain_block
+      = link_to t('admin.domain_blocks.edit'), edit_admin_domain_block_path(@instance.domain_block), class: 'button'
+      = link_to t('admin.domain_blocks.undo'), admin_domain_block_path(@instance.domain_block), class: 'button'
     - else
       = link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path(_domain: @instance.domain), class: 'button'
diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml
index bb441380e..721c55f71 100644
--- a/app/views/admin/reports/index.html.haml
+++ b/app/views/admin/reports/index.html.haml
@@ -59,6 +59,10 @@
                 = fa_icon('camera')
                 = report.media_attachments.count
 
+              - if report.forwarded?
+                ·
+                = t('admin.reports.forwarded_to', domain: target_account.domain)
+
           .report-card__summary__item__assigned
             - if report.assigned_account.present?
               = admin_account_link_to report.assigned_account
diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml
index 4ecc8dc93..167e96c03 100644
--- a/app/views/admin/reports/show.html.haml
+++ b/app/views/admin/reports/show.html.haml
@@ -43,6 +43,16 @@
         %td{ colspan: 2 }
           - if @report.action_taken?
             = table_link_to 'envelope-open', t('admin.reports.reopen'), admin_report_path(@report, outcome: 'reopen'), method: :put
+      - unless @report.target_account.local?
+        %tr
+          %th= t('admin.reports.forwarded')
+          %td{ colspan: 3 }
+            - if @report.forwarded.nil?
+              \-
+            - elsif @report.forwarded?
+              = t('simple_form.yes')
+            - else
+              = t('simple_form.no')
       - if !@report.action_taken_by_account.nil?
         %tr
           %th= t('admin.reports.action_taken_by')
diff --git a/app/views/admin/settings/edit.html.haml b/app/views/admin/settings/edit.html.haml
index 108846ca9..fa8d8441e 100644
--- a/app/views/admin/settings/edit.html.haml
+++ b/app/views/admin/settings/edit.html.haml
@@ -41,7 +41,14 @@
   %hr.spacer/
 
   .fields-group
-    = f.input :enable_bootstrap_timeline_accounts, as: :boolean, wrapper: :with_label, label: t('admin.settings.enable_bootstrap_timeline_accounts.title')
+    = f.input :require_invite_text, as: :boolean, wrapper: :with_label, label: t('admin.settings.registrations.require_invite_text.title'), hint: t('admin.settings.registrations.require_invite_text.desc_html'), disabled: !approved_registrations?
+  .fields-group
+
+  %hr.spacer/
+
+  .fields-group
+    = f.input :enable_bootstrap_timeline_accounts, as: :boolean, wrapper: :with_label, label: t('admin.settings.enable_bootstrap_timeline_accounts.title'), hint: t('admin.settings.enable_bootstrap_timeline_accounts.desc_html')
+
   .fields-group
     = f.input :bootstrap_timeline_accounts, wrapper: :with_block_label, label: t('admin.settings.bootstrap_timeline_accounts.title'), hint: t('admin.settings.bootstrap_timeline_accounts.desc_html'), disabled: !Setting.enable_bootstrap_timeline_accounts
 
diff --git a/app/views/auth/registrations/new.html.haml b/app/views/auth/registrations/new.html.haml
index de541847f..6981195ed 100644
--- a/app/views/auth/registrations/new.html.haml
+++ b/app/views/auth/registrations/new.html.haml
@@ -31,7 +31,7 @@
   - if approved_registrations? && !@invite.present?
     .fields-group
       = f.simple_fields_for :invite_request, resource.invite_request || resource.build_invite_request do |invite_request_fields|
-        = invite_request_fields.input :text, as: :text, wrapper: :with_block_label, required: false
+        = invite_request_fields.input :text, as: :text, wrapper: :with_block_label, required: Setting.require_invite_text
 
   = f.input :invite_code, as: :hidden
 
diff --git a/app/views/notification_mailer/_status.html.haml b/app/views/notification_mailer/_status.html.haml
index e992e5563..9b7e1b65c 100644
--- a/app/views/notification_mailer/_status.html.haml
+++ b/app/views/notification_mailer/_status.html.haml
@@ -26,11 +26,11 @@
                                       = "@#{status.account.acct}"
 
                               - if status.spoiler_text?
-                                %div{ dir: rtl_status?(status) ? 'rtl' : 'ltr' }
+                                %div.auto-dir
                                   %p
                                     = Formatter.instance.format_spoiler(status)
 
-                              %div{ dir: rtl_status?(status) ? 'rtl' : 'ltr' }
+                              %div.auto-dir
                                 = Formatter.instance.format(status)
 
                                 - if status.media_attachments.size > 0
diff --git a/app/views/statuses/_detailed_status.html.haml b/app/views/statuses/_detailed_status.html.haml
index a4dd8534f..4c879472d 100644
--- a/app/views/statuses/_detailed_status.html.haml
+++ b/app/views/statuses/_detailed_status.html.haml
@@ -20,7 +20,7 @@
       %p<
         %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}&nbsp;
         %button.status__content__spoiler-link= t('statuses.show_more')
-    .e-content{ dir: rtl_status?(status) ? 'rtl' : 'ltr' }
+    .e-content
       = Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay)
       - if status.preloadable_poll
         = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do
diff --git a/app/views/statuses/_simple_status.html.haml b/app/views/statuses/_simple_status.html.haml
index cbeefdffe..1b501a3ef 100644
--- a/app/views/statuses/_simple_status.html.haml
+++ b/app/views/statuses/_simple_status.html.haml
@@ -29,7 +29,7 @@
       %p<
         %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}&nbsp;
         %button.status__content__spoiler-link= t('statuses.show_more')
-    .e-content{ dir: rtl_status?(status) ? 'rtl' : 'ltr' }<
+    .e-content<
       = Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay)
       - if status.preloadable_poll
         = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do
diff --git a/app/workers/account_deletion_worker.rb b/app/workers/account_deletion_worker.rb
index 98b67419d..fdf013e01 100644
--- a/app/workers/account_deletion_worker.rb
+++ b/app/workers/account_deletion_worker.rb
@@ -3,7 +3,7 @@
 class AccountDeletionWorker
   include Sidekiq::Worker
 
-  sidekiq_options queue: 'pull'
+  sidekiq_options queue: 'pull', lock: :until_executed
 
   def perform(account_id, options = {})
     reserve_username = options.with_indifferent_access.fetch(:reserve_username, true)
diff --git a/app/workers/account_merging_worker.rb b/app/workers/account_merging_worker.rb
new file mode 100644
index 000000000..8c234e7ac
--- /dev/null
+++ b/app/workers/account_merging_worker.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AccountMergingWorker
+  include Sidekiq::Worker
+
+  sidekiq_options queue: 'pull'
+
+  def perform(account_id)
+    account = Account.find(account_id)
+
+    return true if account.nil? || account.local?
+
+    Account.where(uri: account.uri).where.not(id: account.id).find_each do |duplicate|
+      account.merge_with!(duplicate)
+      duplicate.destroy
+    end
+  end
+end
diff --git a/app/workers/scheduler/feed_cleanup_scheduler.rb b/app/workers/scheduler/feed_cleanup_scheduler.rb
index 99e3440fe..96c17c578 100644
--- a/app/workers/scheduler/feed_cleanup_scheduler.rb
+++ b/app/workers/scheduler/feed_cleanup_scheduler.rb
@@ -15,41 +15,15 @@ class Scheduler::FeedCleanupScheduler
   private
 
   def clean_home_feeds!
-    clean_feeds!(inactive_account_ids, :home)
+    feed_manager.clean_feeds!(:home, inactive_account_ids)
   end
 
   def clean_list_feeds!
-    clean_feeds!(inactive_list_ids, :list)
+    feed_manager.clean_feeds!(:list, inactive_list_ids)
   end
 
   def clean_direct_feeds!
-    clean_feeds!(inactive_account_ids, :direct)
-  end
-
-  def clean_feeds!(ids, type)
-    reblogged_id_sets = {}
-
-    redis.pipelined do
-      ids.each do |feed_id|
-        redis.del(feed_manager.key(type, feed_id))
-        reblog_key = feed_manager.key(type, feed_id, 'reblogs')
-        # We collect a future for this: we don't block while getting
-        # it, but we can iterate over it later.
-        reblogged_id_sets[feed_id] = redis.zrange(reblog_key, 0, -1)
-        redis.del(reblog_key)
-      end
-    end
-
-    # Remove all of the reblog tracking keys we just removed the
-    # references to.
-    redis.pipelined do
-      reblogged_id_sets.each do |feed_id, future|
-        future.value.each do |reblogged_id|
-          reblog_set_key = feed_manager.key(type, feed_id, "reblogs:#{reblogged_id}")
-          redis.del(reblog_set_key)
-        end
-      end
-    end
+    feed_manager.clean_feeds!(:direct, inactive_account_ids)
   end
 
   def inactive_account_ids
diff --git a/app/workers/scheduler/instance_refresh_scheduler.rb b/app/workers/scheduler/instance_refresh_scheduler.rb
new file mode 100644
index 000000000..917404bec
--- /dev/null
+++ b/app/workers/scheduler/instance_refresh_scheduler.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class Scheduler::InstanceRefreshScheduler
+  include Sidekiq::Worker
+
+  sidekiq_options lock: :until_executed, retry: 0
+
+  def perform
+    Instance.refresh
+  end
+end