about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/controllers/admin/statuses_controller.rb9
-rw-r--r--app/controllers/api/base_controller.rb4
-rw-r--r--app/controllers/api/v1/follow_requests_controller.rb1
-rw-r--r--app/controllers/application_controller.rb4
-rw-r--r--app/controllers/auth/sessions_controller.rb15
-rw-r--r--app/controllers/oauth/authorizations_controller.rb14
-rw-r--r--app/controllers/settings/notifications_controller.rb2
-rw-r--r--app/controllers/settings/preferences_controller.rb2
-rw-r--r--app/controllers/statuses_controller.rb1
-rw-r--r--app/helpers/admin/account_moderation_notes_helper.rb8
-rw-r--r--app/helpers/home_helper.rb16
-rw-r--r--app/helpers/settings_helper.rb4
-rw-r--r--app/helpers/stream_entries_helper.rb4
-rw-r--r--app/javascript/core/settings.js24
-rw-r--r--app/javascript/flavours/glitch/actions/dropdown_menu.js4
-rw-r--r--app/javascript/flavours/glitch/actions/notifications.js24
-rw-r--r--app/javascript/flavours/glitch/components/dropdown_menu.js32
-rw-r--r--app/javascript/flavours/glitch/components/media_gallery.js17
-rw-r--r--app/javascript/flavours/glitch/components/scrollable_list.js6
-rw-r--r--app/javascript/flavours/glitch/components/status.js1
-rw-r--r--app/javascript/flavours/glitch/components/status_action_bar.js9
-rw-r--r--app/javascript/flavours/glitch/containers/dropdown_menu_container.js7
-rw-r--r--app/javascript/flavours/glitch/features/account/components/action_bar.js29
-rw-r--r--app/javascript/flavours/glitch/features/account/components/header.js25
-rw-r--r--app/javascript/flavours/glitch/features/account_timeline/index.js5
-rw-r--r--app/javascript/flavours/glitch/features/bookmarked_statuses/index.js5
-rw-r--r--app/javascript/flavours/glitch/features/community_timeline/index.js5
-rw-r--r--app/javascript/flavours/glitch/features/composer/index.js16
-rw-r--r--app/javascript/flavours/glitch/features/composer/textarea/suggestions/item/index.js2
-rw-r--r--app/javascript/flavours/glitch/features/direct_timeline/index.js5
-rw-r--r--app/javascript/flavours/glitch/features/domain_blocks/index.js6
-rw-r--r--app/javascript/flavours/glitch/features/drawer/header/index.js11
-rw-r--r--app/javascript/flavours/glitch/features/drawer/index.js8
-rw-r--r--app/javascript/flavours/glitch/features/emoji_picker/index.js1
-rw-r--r--app/javascript/flavours/glitch/features/favourited_statuses/index.js5
-rw-r--r--app/javascript/flavours/glitch/features/favourites/index.js6
-rw-r--r--app/javascript/flavours/glitch/features/follow_requests/index.js6
-rw-r--r--app/javascript/flavours/glitch/features/followers/index.js6
-rw-r--r--app/javascript/flavours/glitch/features/hashtag_timeline/index.js5
-rw-r--r--app/javascript/flavours/glitch/features/local_settings/navigation/index.js19
-rw-r--r--app/javascript/flavours/glitch/features/local_settings/page/index.js123
-rw-r--r--app/javascript/flavours/glitch/features/mutes/index.js6
-rw-r--r--app/javascript/flavours/glitch/features/notifications/index.js24
-rw-r--r--app/javascript/flavours/glitch/features/pinned_statuses/index.js5
-rw-r--r--app/javascript/flavours/glitch/features/public_timeline/index.js5
-rw-r--r--app/javascript/flavours/glitch/features/reblogs/index.js6
-rw-r--r--app/javascript/flavours/glitch/features/standalone/community_timeline/index.js5
-rw-r--r--app/javascript/flavours/glitch/features/standalone/hashtag_timeline/index.js5
-rw-r--r--app/javascript/flavours/glitch/features/status/components/action_bar.js9
-rw-r--r--app/javascript/flavours/glitch/features/status/components/card.js177
-rw-r--r--app/javascript/flavours/glitch/features/status/components/detailed_status.js1
-rw-r--r--app/javascript/flavours/glitch/features/status/index.js2
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/columns_area.js12
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/media_modal.js2
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/report_modal.js1
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/tabs_bar.js26
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/video_modal.js2
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/zoomable_image.js1
-rw-r--r--app/javascript/flavours/glitch/features/ui/index.js47
-rw-r--r--app/javascript/flavours/glitch/features/video/index.js17
-rw-r--r--app/javascript/flavours/glitch/locales/ja.js29
-rw-r--r--app/javascript/flavours/glitch/middleware/sounds.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/compose.js6
-rw-r--r--app/javascript/flavours/glitch/reducers/dropdown_menu.js4
-rw-r--r--app/javascript/flavours/glitch/reducers/local_settings.js5
-rw-r--r--app/javascript/flavours/glitch/reducers/notifications.js66
-rw-r--r--app/javascript/flavours/glitch/styles/_mixins.scss5
-rw-r--r--app/javascript/flavours/glitch/styles/accessibility.scss13
-rw-r--r--app/javascript/flavours/glitch/styles/accounts.scss14
-rw-r--r--app/javascript/flavours/glitch/styles/admin.scss183
-rw-r--r--app/javascript/flavours/glitch/styles/basics.scss7
-rw-r--r--app/javascript/flavours/glitch/styles/components/accounts.scss5
-rw-r--r--app/javascript/flavours/glitch/styles/components/boost.scss7
-rw-r--r--app/javascript/flavours/glitch/styles/components/drawer.scss3
-rw-r--r--app/javascript/flavours/glitch/styles/components/index.scss28
-rw-r--r--app/javascript/flavours/glitch/styles/components/media.scss21
-rw-r--r--app/javascript/flavours/glitch/styles/components/metadata.scss22
-rw-r--r--app/javascript/flavours/glitch/styles/components/status.scss69
-rw-r--r--app/javascript/flavours/glitch/styles/containers.scss29
-rw-r--r--app/javascript/flavours/glitch/styles/forms.scss383
-rw-r--r--app/javascript/flavours/glitch/styles/index.scss1
-rw-r--r--app/javascript/flavours/glitch/styles/mastodon-light.scss219
-rw-r--r--app/javascript/flavours/glitch/styles/mastodon-light/diff.scss318
-rw-r--r--app/javascript/flavours/glitch/styles/mastodon-light/variables.scss38
-rw-r--r--app/javascript/flavours/glitch/styles/rtl.scss8
-rw-r--r--app/javascript/flavours/glitch/styles/stream_entries.scss29
-rw-r--r--app/javascript/flavours/glitch/util/emoji/index.js4
-rw-r--r--app/javascript/flavours/glitch/util/initial_state.js1
-rw-r--r--app/javascript/mastodon/actions/dropdown_menu.js4
-rw-r--r--app/javascript/mastodon/components/__tests__/__snapshots__/button-test.js.snap6
-rw-r--r--app/javascript/mastodon/components/account.js4
-rw-r--r--app/javascript/mastodon/components/column_header.js4
-rw-r--r--app/javascript/mastodon/components/domain.js4
-rw-r--r--app/javascript/mastodon/components/dropdown_menu.js32
-rw-r--r--app/javascript/mastodon/components/load_gap.js4
-rw-r--r--app/javascript/mastodon/components/media_gallery.js4
-rw-r--r--app/javascript/mastodon/components/relative_timestamp.js4
-rw-r--r--app/javascript/mastodon/components/status.js4
-rw-r--r--app/javascript/mastodon/components/status_action_bar.js4
-rw-r--r--app/javascript/mastodon/containers/dropdown_menu_container.js5
-rw-r--r--app/javascript/mastodon/features/account/components/action_bar.js25
-rw-r--r--app/javascript/mastodon/features/account/components/header.js19
-rw-r--r--app/javascript/mastodon/features/account_gallery/index.js4
-rw-r--r--app/javascript/mastodon/features/account_timeline/index.js4
-rw-r--r--app/javascript/mastodon/features/blocks/index.js4
-rw-r--r--app/javascript/mastodon/features/community_timeline/components/column_settings.js4
-rw-r--r--app/javascript/mastodon/features/community_timeline/index.js4
-rw-r--r--app/javascript/mastodon/features/compose/components/action_bar.js4
-rw-r--r--app/javascript/mastodon/features/compose/components/compose_form.js4
-rw-r--r--app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js5
-rw-r--r--app/javascript/mastodon/features/compose/components/privacy_dropdown.js4
-rw-r--r--app/javascript/mastodon/features/compose/components/reply_indicator.js4
-rw-r--r--app/javascript/mastodon/features/compose/components/search.js4
-rw-r--r--app/javascript/mastodon/features/compose/components/upload.js4
-rw-r--r--app/javascript/mastodon/features/compose/components/upload_button.js4
-rw-r--r--app/javascript/mastodon/features/compose/index.js4
-rw-r--r--app/javascript/mastodon/features/compose/util/url_regex.js2
-rw-r--r--app/javascript/mastodon/features/direct_timeline/components/column_settings.js4
-rw-r--r--app/javascript/mastodon/features/direct_timeline/index.js4
-rw-r--r--app/javascript/mastodon/features/domain_blocks/index.js4
-rw-r--r--app/javascript/mastodon/features/emoji/__tests__/emoji-test.js5
-rw-r--r--app/javascript/mastodon/features/emoji/emoji.js4
-rw-r--r--app/javascript/mastodon/features/favourited_statuses/index.js4
-rw-r--r--app/javascript/mastodon/features/favourites/index.js4
-rw-r--r--app/javascript/mastodon/features/follow_requests/components/account_authorize.js4
-rw-r--r--app/javascript/mastodon/features/follow_requests/index.js4
-rw-r--r--app/javascript/mastodon/features/followers/index.js4
-rw-r--r--app/javascript/mastodon/features/following/index.js4
-rw-r--r--app/javascript/mastodon/features/getting_started/index.js62
-rw-r--r--app/javascript/mastodon/features/hashtag_timeline/index.js4
-rw-r--r--app/javascript/mastodon/features/home_timeline/components/column_settings.js4
-rw-r--r--app/javascript/mastodon/features/home_timeline/index.js4
-rw-r--r--app/javascript/mastodon/features/keyboard_shortcuts/index.js4
-rw-r--r--app/javascript/mastodon/features/list_editor/components/account.js4
-rw-r--r--app/javascript/mastodon/features/list_editor/components/search.js4
-rw-r--r--app/javascript/mastodon/features/list_editor/index.js4
-rw-r--r--app/javascript/mastodon/features/list_timeline/index.js10
-rw-r--r--app/javascript/mastodon/features/lists/components/new_list_form.js4
-rw-r--r--app/javascript/mastodon/features/lists/index.js4
-rw-r--r--app/javascript/mastodon/features/mutes/index.js4
-rw-r--r--app/javascript/mastodon/features/notifications/components/notification.js4
-rw-r--r--app/javascript/mastodon/features/notifications/index.js4
-rw-r--r--app/javascript/mastodon/features/pinned_statuses/index.js4
-rw-r--r--app/javascript/mastodon/features/public_timeline/index.js4
-rw-r--r--app/javascript/mastodon/features/reblogs/index.js4
-rw-r--r--app/javascript/mastodon/features/standalone/community_timeline/index.js4
-rw-r--r--app/javascript/mastodon/features/standalone/hashtag_timeline/index.js4
-rw-r--r--app/javascript/mastodon/features/standalone/public_timeline/index.js4
-rw-r--r--app/javascript/mastodon/features/status/components/action_bar.js4
-rw-r--r--app/javascript/mastodon/features/status/index.js4
-rw-r--r--app/javascript/mastodon/features/ui/components/boost_modal.js4
-rw-r--r--app/javascript/mastodon/features/ui/components/columns_area.js14
-rw-r--r--app/javascript/mastodon/features/ui/components/confirmation_modal.js4
-rw-r--r--app/javascript/mastodon/features/ui/components/embed_modal.js4
-rw-r--r--app/javascript/mastodon/features/ui/components/focal_point_modal.js4
-rw-r--r--app/javascript/mastodon/features/ui/components/media_modal.js6
-rw-r--r--app/javascript/mastodon/features/ui/components/mute_modal.js4
-rw-r--r--app/javascript/mastodon/features/ui/components/onboarding_modal.js4
-rw-r--r--app/javascript/mastodon/features/ui/components/report_modal.js5
-rw-r--r--app/javascript/mastodon/features/ui/components/tabs_bar.js4
-rw-r--r--app/javascript/mastodon/features/ui/components/video_modal.js2
-rw-r--r--app/javascript/mastodon/features/ui/components/zoomable_image.js1
-rw-r--r--app/javascript/mastodon/features/ui/index.js4
-rw-r--r--app/javascript/mastodon/features/ui/util/react_router_helpers.js2
-rw-r--r--app/javascript/mastodon/features/video/index.js7
-rw-r--r--app/javascript/mastodon/initial_state.js2
-rw-r--r--app/javascript/mastodon/locales/ar.json45
-rw-r--r--app/javascript/mastodon/locales/ast.json289
-rw-r--r--app/javascript/mastodon/locales/bg.json1
-rw-r--r--app/javascript/mastodon/locales/ca.json65
-rw-r--r--app/javascript/mastodon/locales/co.json47
-rw-r--r--app/javascript/mastodon/locales/cs.json49
-rw-r--r--app/javascript/mastodon/locales/cy.json337
-rw-r--r--app/javascript/mastodon/locales/da.json59
-rw-r--r--app/javascript/mastodon/locales/de.json77
-rw-r--r--app/javascript/mastodon/locales/defaultMessages.json15
-rw-r--r--app/javascript/mastodon/locales/el.json3
-rw-r--r--app/javascript/mastodon/locales/en.json1
-rw-r--r--app/javascript/mastodon/locales/eo.json1
-rw-r--r--app/javascript/mastodon/locales/es.json121
-rw-r--r--app/javascript/mastodon/locales/eu.json55
-rw-r--r--app/javascript/mastodon/locales/fa.json55
-rw-r--r--app/javascript/mastodon/locales/fi.json1
-rw-r--r--app/javascript/mastodon/locales/fr.json47
-rw-r--r--app/javascript/mastodon/locales/gl.json5
-rw-r--r--app/javascript/mastodon/locales/he.json1
-rw-r--r--app/javascript/mastodon/locales/hr.json1
-rw-r--r--app/javascript/mastodon/locales/hu.json1
-rw-r--r--app/javascript/mastodon/locales/hy.json1
-rw-r--r--app/javascript/mastodon/locales/id.json1
-rw-r--r--app/javascript/mastodon/locales/io.json1
-rw-r--r--app/javascript/mastodon/locales/it.json55
-rw-r--r--app/javascript/mastodon/locales/ja.json53
-rw-r--r--app/javascript/mastodon/locales/ka.json1
-rw-r--r--app/javascript/mastodon/locales/ko.json53
-rw-r--r--app/javascript/mastodon/locales/locale-data/co.js108
-rw-r--r--app/javascript/mastodon/locales/nl.json71
-rw-r--r--app/javascript/mastodon/locales/no.json1
-rw-r--r--app/javascript/mastodon/locales/oc.json53
-rw-r--r--app/javascript/mastodon/locales/pl.json3
-rw-r--r--app/javascript/mastodon/locales/pt-BR.json1
-rw-r--r--app/javascript/mastodon/locales/pt.json1
-rw-r--r--app/javascript/mastodon/locales/ro.json337
-rw-r--r--app/javascript/mastodon/locales/ru.json57
-rw-r--r--app/javascript/mastodon/locales/sk.json61
-rw-r--r--app/javascript/mastodon/locales/sl.json1
-rw-r--r--app/javascript/mastodon/locales/sr-Latn.json1
-rw-r--r--app/javascript/mastodon/locales/sr.json255
-rw-r--r--app/javascript/mastodon/locales/sv.json1
-rw-r--r--app/javascript/mastodon/locales/ta.json337
-rw-r--r--app/javascript/mastodon/locales/te.json59
-rw-r--r--app/javascript/mastodon/locales/th.json1
-rw-r--r--app/javascript/mastodon/locales/tr.json3
-rw-r--r--app/javascript/mastodon/locales/uk.json1
-rw-r--r--app/javascript/mastodon/locales/whitelist_cy.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_ro.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_ta.json2
-rw-r--r--app/javascript/mastodon/locales/zh-CN.json1
-rw-r--r--app/javascript/mastodon/locales/zh-HK.json1
-rw-r--r--app/javascript/mastodon/locales/zh-TW.json71
-rw-r--r--app/javascript/mastodon/reducers/dropdown_menu.js4
-rw-r--r--app/javascript/packs/public.js3
-rw-r--r--app/javascript/styles/mastodon/accessibility.scss2
-rw-r--r--app/javascript/styles/mastodon/accounts.scss14
-rw-r--r--app/javascript/styles/mastodon/admin.scss183
-rw-r--r--app/javascript/styles/mastodon/basics.scss7
-rw-r--r--app/javascript/styles/mastodon/boost.scss7
-rw-r--r--app/javascript/styles/mastodon/components.scss62
-rw-r--r--app/javascript/styles/mastodon/containers.scss29
-rw-r--r--app/javascript/styles/mastodon/forms.scss361
-rw-r--r--app/javascript/styles/mastodon/reset.scss5
-rw-r--r--app/lib/activitypub/activity/update.rb1
-rw-r--r--app/lib/formatter.rb22
-rw-r--r--app/lib/request.rb6
-rw-r--r--app/models/account.rb54
-rw-r--r--app/models/concerns/remotable.rb2
-rw-r--r--app/models/report.rb4
-rw-r--r--app/models/status.rb2
-rw-r--r--app/models/user.rb4
-rw-r--r--app/serializers/initial_state_serializer.rb1
-rw-r--r--app/serializers/rest/account_serializer.rb4
-rw-r--r--app/services/activitypub/process_account_service.rb5
-rw-r--r--app/services/fetch_link_card_service.rb20
-rw-r--r--app/services/fetch_oembed_service.rb2
-rw-r--r--app/services/report_service.rb3
-rw-r--r--app/services/update_account_service.rb12
-rw-r--r--app/services/verify_link_service.rb48
-rw-r--r--app/views/about/_registration.html.haml11
-rw-r--r--app/views/about/more.html.haml2
-rw-r--r--app/views/about/show.html.haml4
-rw-r--r--app/views/accounts/_bio.html.haml7
-rw-r--r--app/views/accounts/_header.html.haml9
-rw-r--r--app/views/admin/change_emails/show.html.haml11
-rw-r--r--app/views/admin/custom_emojis/new.html.haml5
-rw-r--r--app/views/admin/domain_blocks/new.html.haml13
-rw-r--r--app/views/admin/email_domain_blocks/new.html.haml3
-rw-r--r--app/views/admin/reports/_account.html.haml19
-rw-r--r--app/views/admin/reports/_report.html.haml29
-rw-r--r--app/views/admin/reports/index.html.haml52
-rw-r--r--app/views/admin/settings/edit.html.haml63
-rw-r--r--app/views/admin/statuses/show.html.haml27
-rw-r--r--app/views/auth/confirmations/finish_signup.html.haml3
-rw-r--r--app/views/auth/confirmations/new.html.haml3
-rw-r--r--app/views/auth/passwords/edit.html.haml6
-rw-r--r--app/views/auth/passwords/new.html.haml3
-rw-r--r--app/views/auth/registrations/edit.html.haml20
-rw-r--r--app/views/auth/registrations/new.html.haml22
-rw-r--r--app/views/auth/sessions/new.html.haml12
-rw-r--r--app/views/auth/sessions/two_factor.html.haml3
-rw-r--r--app/views/filters/_fields.html.haml12
-rw-r--r--app/views/invites/_form.html.haml8
-rw-r--r--app/views/invites/index.html.haml2
-rw-r--r--app/views/layouts/modal.html.haml2
-rw-r--r--app/views/settings/applications/_fields.html.haml6
-rw-r--r--app/views/settings/exports/show.html.haml2
-rw-r--r--app/views/settings/imports/show.html.haml4
-rw-r--r--app/views/settings/notifications/show.html.haml3
-rw-r--r--app/views/settings/preferences/show.html.haml27
-rw-r--r--app/views/settings/profiles/show.html.haml53
-rw-r--r--app/views/settings/two_factor_authentication/confirmations/new.html.haml3
-rw-r--r--app/views/settings/two_factor_authentications/show.html.haml2
-rw-r--r--app/views/stream_entries/_detailed_status.html.haml13
-rw-r--r--app/views/stream_entries/_simple_status.html.haml13
-rw-r--r--app/views/stream_entries/_status.html.haml7
-rw-r--r--app/views/stream_entries/embed.html.haml2
-rw-r--r--app/workers/verify_account_links_worker.rb20
286 files changed, 4908 insertions, 2155 deletions
diff --git a/app/controllers/admin/statuses_controller.rb b/app/controllers/admin/statuses_controller.rb
index a69f12084..62f49806c 100644
--- a/app/controllers/admin/statuses_controller.rb
+++ b/app/controllers/admin/statuses_controller.rb
@@ -22,6 +22,15 @@ module Admin
       @form     = Form::StatusBatch.new
     end
 
+    def show
+      authorize :status, :index?
+
+      @statuses = @account.statuses.where(id: params[:id])
+      authorize @statuses[0], :show?
+
+      @form = Form::StatusBatch.new
+    end
+
     def create
       authorize :status, :update?
 
diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb
index 0b3735087..90f42251e 100644
--- a/app/controllers/api/base_controller.rb
+++ b/app/controllers/api/base_controller.rb
@@ -53,10 +53,6 @@ class Api::BaseController < ApplicationController
     [params[:limit].to_i.abs, default_limit * 2].min
   end
 
-  def truthy_param?(key)
-    ActiveModel::Type::Boolean.new.cast(params[key])
-  end
-
   def current_resource_owner
     @current_user ||= User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
   end
diff --git a/app/controllers/api/v1/follow_requests_controller.rb b/app/controllers/api/v1/follow_requests_controller.rb
index 313fe2f81..e9aca5f8a 100644
--- a/app/controllers/api/v1/follow_requests_controller.rb
+++ b/app/controllers/api/v1/follow_requests_controller.rb
@@ -13,6 +13,7 @@ class Api::V1::FollowRequestsController < Api::BaseController
 
   def authorize
     AuthorizeFollowService.new.call(account, current_account)
+    NotifyService.new.call(current_account, Follow.find_by(account: account, target_account: current_account))
     render_empty
   end
 
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 8ffc31bb4..dca6c5a5a 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -128,6 +128,10 @@ class ApplicationController < ActionController::Base
 
   protected
 
+  def truthy_param?(key)
+    ActiveModel::Type::Boolean.new.cast(params[key])
+  end
+
   def forbidden
     respond_with_error(403)
   end
diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb
index 7cd46662f..d4a659409 100644
--- a/app/controllers/auth/sessions_controller.rb
+++ b/app/controllers/auth/sessions_controller.rb
@@ -11,6 +11,7 @@ class Auth::SessionsController < Devise::SessionsController
   prepend_before_action :set_pack
   before_action :set_instance_presenter, only: [:new]
   before_action :set_body_classes
+  after_action :clear_site_data, only: [:destroy]
 
   def new
     Devise.omniauth_configs.each do |provider, config|
@@ -28,8 +29,10 @@ class Auth::SessionsController < Devise::SessionsController
   end
 
   def destroy
+    tmp_stored_location = stored_location_for(:user)
     super
     flash.delete(:notice)
+    store_location_for(:user, tmp_stored_location) if continue_after?
   end
 
   protected
@@ -126,4 +129,16 @@ class Auth::SessionsController < Devise::SessionsController
     end
     paths
   end
+
+  def clear_site_data
+    return if continue_after?
+
+    # Should be '"*"' but that doesn't work in Chrome (neither does '"executionContexts"')
+    # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data
+    response.headers['Clear-Site-Data'] = '"cache", "cookies"'
+  end
+
+  def continue_after?
+    truthy_param?(:continue)
+  end
 end
diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb
index eb977510b..f6f5d1ecc 100644
--- a/app/controllers/oauth/authorizations_controller.rb
+++ b/app/controllers/oauth/authorizations_controller.rb
@@ -18,4 +18,18 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
   def set_pack
     use_pack 'auth'
   end
+
+  def render_success
+    if skip_authorization? || (matching_token? && !truthy_param?('force_login'))
+      redirect_or_render authorize_response
+    elsif Doorkeeper.configuration.api_only
+      render json: pre_auth
+    else
+      render :new
+    end
+  end
+
+  def truthy_param?(key)
+    ActiveModel::Type::Boolean.new.cast(params[key])
+  end
 end
diff --git a/app/controllers/settings/notifications_controller.rb b/app/controllers/settings/notifications_controller.rb
index 6286e3ebf..68ebddfc9 100644
--- a/app/controllers/settings/notifications_controller.rb
+++ b/app/controllers/settings/notifications_controller.rb
@@ -21,7 +21,7 @@ class Settings::NotificationsController < Settings::BaseController
 
   def user_settings_params
     params.require(:user).permit(
-      notification_emails: %i(follow follow_request reblog favourite mention digest),
+      notification_emails: %i(follow follow_request reblog favourite mention digest report),
       interactions: %i(must_be_follower must_be_following must_be_following_dm)
     )
   end
diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb
index 4733170dc..d60e6a89f 100644
--- a/app/controllers/settings/preferences_controller.rb
+++ b/app/controllers/settings/preferences_controller.rb
@@ -42,7 +42,7 @@ class Settings::PreferencesController < Settings::BaseController
       :setting_system_font_ui,
       :setting_noindex,
       :setting_hide_network,
-      notification_emails: %i(follow follow_request reblog favourite mention digest),
+      notification_emails: %i(follow follow_request reblog favourite mention digest report),
       interactions: %i(must_be_follower must_be_following)
     )
   end
diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb
index cf750f4bb..145e77918 100644
--- a/app/controllers/statuses_controller.rb
+++ b/app/controllers/statuses_controller.rb
@@ -60,6 +60,7 @@ class StatusesController < ApplicationController
     skip_session!
     expires_in 180, public: true
     response.headers['X-Frame-Options'] = 'ALLOWALL'
+    @autoplay = ActiveModel::Type::Boolean.new.cast(params[:autoplay])
 
     render 'stream_entries/embed', layout: 'embedded'
   end
diff --git a/app/helpers/admin/account_moderation_notes_helper.rb b/app/helpers/admin/account_moderation_notes_helper.rb
index 49e764cef..4d8f0352e 100644
--- a/app/helpers/admin/account_moderation_notes_helper.rb
+++ b/app/helpers/admin/account_moderation_notes_helper.rb
@@ -2,7 +2,9 @@
 
 module Admin::AccountModerationNotesHelper
   def admin_account_link_to(account)
-    link_to admin_account_path(account.id), class: name_tag_classes(account) do
+    return if account.nil?
+
+    link_to admin_account_path(account.id), class: name_tag_classes(account), title: account.acct do
       safe_join([
                   image_tag(account.avatar.url, width: 15, height: 15, alt: display_name(account), class: 'avatar'),
                   content_tag(:span, account.acct, class: 'username'),
@@ -11,7 +13,9 @@ module Admin::AccountModerationNotesHelper
   end
 
   def admin_account_inline_link_to(account)
-    link_to admin_account_path(account.id), class: name_tag_classes(account, true) do
+    return if account.nil?
+
+    link_to admin_account_path(account.id), class: name_tag_classes(account, true), title: account.acct do
       content_tag(:span, account.acct, class: 'username')
     end
   end
diff --git a/app/helpers/home_helper.rb b/app/helpers/home_helper.rb
index f5b501235..ba7c443c2 100644
--- a/app/helpers/home_helper.rb
+++ b/app/helpers/home_helper.rb
@@ -7,13 +7,13 @@ module HomeHelper
     }
   end
 
-  def account_link_to(account, button = '')
+  def account_link_to(account, button = '', size: 36, path: nil)
     content_tag(:div, class: 'account') do
       content_tag(:div, class: 'account__wrapper') do
         section = if account.nil?
                     content_tag(:div, class: 'account__display-name') do
                       content_tag(:div, class: 'account__avatar-wrapper') do
-                        content_tag(:div, '', class: 'account__avatar', style: "background-image: url(#{full_asset_url('avatars/original/missing.png', skip_pipeline: true)})")
+                        content_tag(:div, '', class: 'account__avatar', style: "width: #{size}px; height: #{size}px; background-size: #{size}px #{size}px; background-image: url(#{full_asset_url('avatars/original/missing.png', skip_pipeline: true)})")
                       end +
                         content_tag(:span, class: 'display-name') do
                           content_tag(:strong, t('about.contact_missing')) +
@@ -21,9 +21,9 @@ module HomeHelper
                         end
                     end
                   else
-                    link_to(TagManager.instance.url_for(account), class: 'account__display-name') do
+                    link_to(path || TagManager.instance.url_for(account), class: 'account__display-name') do
                       content_tag(:div, class: 'account__avatar-wrapper') do
-                        content_tag(:div, '', class: 'account__avatar', style: "background-image: url(#{account.avatar.url})")
+                        content_tag(:div, '', class: 'account__avatar', style: "width: #{size}px; height: #{size}px; background-size: #{size}px #{size}px; background-image: url(#{account.avatar.url})")
                       end +
                         content_tag(:span, class: 'display-name') do
                           content_tag(:bdi) do
@@ -48,4 +48,12 @@ module HomeHelper
       '1+'
     end
   end
+
+  def custom_field_classes(field)
+    if field.verified?
+      'verified'
+    else
+      'emojify'
+    end
+  end
 end
diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb
index 14ca2333e..ed873ceed 100644
--- a/app/helpers/settings_helper.rb
+++ b/app/helpers/settings_helper.rb
@@ -8,6 +8,8 @@ module SettingsHelper
     bg: 'Български',
     ca: 'Català',
     co: 'Corsu',
+    cs: 'Čeština',
+    cy: 'Cymraeg',
     da: 'Dansk',
     de: 'Deutsch',
     el: 'Ελληνικά',
@@ -34,12 +36,14 @@ module SettingsHelper
     pl: 'Polszczyzna',
     pt: 'Português',
     'pt-BR': 'Português do Brasil',
+    ro: 'Limba română',
     ru: 'Русский',
     sk: 'Slovenčina',
     sl: 'Slovenščina',
     sr: 'Српски',
     'sr-Latn': 'Srpski (latinica)',
     sv: 'Svenska',
+    ta: 'தமிழ்',
     te: 'తెలుగు',
     th: 'ภาษาไทย',
     tr: 'Türkçe',
diff --git a/app/helpers/stream_entries_helper.rb b/app/helpers/stream_entries_helper.rb
index 187bfe4a0..ac655f622 100644
--- a/app/helpers/stream_entries_helper.rb
+++ b/app/helpers/stream_entries_helper.rb
@@ -22,12 +22,12 @@ module StreamEntriesHelper
         link_to account_unfollow_path(account), class: 'button logo-button button--destructive', data: { method: :post } do
           safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('accounts.unfollow')])
         end
-      else
+      elsif !(account.memorial? || account.moved?)
         link_to account_follow_path(account), class: 'button logo-button', data: { method: :post } do
           safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('accounts.follow')])
         end
       end
-    else
+    elsif !(account.memorial? || account.moved?)
       link_to account_remote_follow_path(account), class: 'button logo-button modal-button', target: '_new' do
         safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('accounts.follow')])
       end
diff --git a/app/javascript/core/settings.js b/app/javascript/core/settings.js
index e48fcb9b1..377c899dc 100644
--- a/app/javascript/core/settings.js
+++ b/app/javascript/core/settings.js
@@ -50,3 +50,27 @@ delegate(document, '#account_locked', 'change', ({ target }) => {
     lock.style.display = 'none';
   }
 });
+
+delegate(document, '.input-copy input', 'click', ({ target }) => {
+  target.select();
+});
+
+delegate(document, '.input-copy button', 'click', ({ target }) => {
+  const input = target.parentNode.querySelector('input');
+
+  input.focus();
+  input.select();
+
+  try {
+    if (document.execCommand('copy')) {
+      input.blur();
+      target.parentNode.classList.add('copied');
+
+    setTimeout(() => {
+        target.parentNode.classList.remove('copied');
+      }, 700);
+    }
+  } catch (err) {
+    console.error(err);
+  }
+});
diff --git a/app/javascript/flavours/glitch/actions/dropdown_menu.js b/app/javascript/flavours/glitch/actions/dropdown_menu.js
index 217ba4e74..14f2939c7 100644
--- a/app/javascript/flavours/glitch/actions/dropdown_menu.js
+++ b/app/javascript/flavours/glitch/actions/dropdown_menu.js
@@ -1,8 +1,8 @@
 export const DROPDOWN_MENU_OPEN = 'DROPDOWN_MENU_OPEN';
 export const DROPDOWN_MENU_CLOSE = 'DROPDOWN_MENU_CLOSE';
 
-export function openDropdownMenu(id, placement) {
-  return { type: DROPDOWN_MENU_OPEN, id, placement };
+export function openDropdownMenu(id, placement, keyboard) {
+  return { type: DROPDOWN_MENU_OPEN, id, placement, keyboard };
 }
 
 export function closeDropdownMenu(id) {
diff --git a/app/javascript/flavours/glitch/actions/notifications.js b/app/javascript/flavours/glitch/actions/notifications.js
index e88eda78f..fb84cd01e 100644
--- a/app/javascript/flavours/glitch/actions/notifications.js
+++ b/app/javascript/flavours/glitch/actions/notifications.js
@@ -25,6 +25,11 @@ export const NOTIFICATIONS_EXPAND_FAIL    = 'NOTIFICATIONS_EXPAND_FAIL';
 export const NOTIFICATIONS_CLEAR      = 'NOTIFICATIONS_CLEAR';
 export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP';
 
+export const NOTIFICATIONS_MOUNT   = 'NOTIFICATIONS_MOUNT';
+export const NOTIFICATIONS_UNMOUNT = 'NOTIFICATIONS_UNMOUNT';
+
+export const NOTIFICATIONS_SET_VISIBILITY = 'NOTIFICATIONS_SET_VISIBILITY';
+
 defineMessages({
   mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' },
 });
@@ -216,3 +221,22 @@ export function deleteMarkedNotificationsSuccess() {
     type: NOTIFICATIONS_DELETE_MARKED_SUCCESS,
   };
 };
+
+export function mountNotifications() {
+  return {
+    type: NOTIFICATIONS_MOUNT,
+  };
+};
+
+export function unmountNotifications() {
+  return {
+    type: NOTIFICATIONS_UNMOUNT,
+  };
+};
+
+export function notificationsSetVisibility(visibility) {
+  return {
+    type: NOTIFICATIONS_SET_VISIBILITY,
+    visibility: visibility,
+  };
+};
diff --git a/app/javascript/flavours/glitch/components/dropdown_menu.js b/app/javascript/flavours/glitch/components/dropdown_menu.js
index 1c2b0bf25..05611c135 100644
--- a/app/javascript/flavours/glitch/components/dropdown_menu.js
+++ b/app/javascript/flavours/glitch/components/dropdown_menu.js
@@ -23,6 +23,7 @@ class DropdownMenu extends React.PureComponent {
     placement: PropTypes.string,
     arrowOffsetLeft: PropTypes.string,
     arrowOffsetTop: PropTypes.string,
+    openedViaKeyboard: PropTypes.bool,
   };
 
   static defaultProps = {
@@ -42,13 +43,15 @@ class DropdownMenu extends React.PureComponent {
 
   componentDidMount () {
     document.addEventListener('click', this.handleDocumentClick, false);
+    document.addEventListener('keydown', this.handleKeyDown, false);
     document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
-    if (this.focusedItem) this.focusedItem.focus();
+    if (this.focusedItem && this.props.openedViaKeyboard) this.focusedItem.focus();
     this.setState({ mounted: true });
   }
 
   componentWillUnmount () {
     document.removeEventListener('click', this.handleDocumentClick, false);
+    document.removeEventListener('keydown', this.handleKeyDown, false);
     document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
   }
 
@@ -62,13 +65,10 @@ class DropdownMenu extends React.PureComponent {
 
   handleKeyDown = e => {
     const items = Array.from(this.node.getElementsByTagName('a'));
-    const index = items.indexOf(e.currentTarget);
+    const index = items.indexOf(document.activeElement);
     let element;
 
     switch(e.key) {
-    case 'Enter':
-      this.handleClick(e);
-      break;
     case 'ArrowDown':
       element = items[index+1];
       if (element) {
@@ -96,6 +96,12 @@ class DropdownMenu extends React.PureComponent {
     }
   }
 
+  handleItemKeyDown = e => {
+    if (e.key === 'Enter') {
+      this.handleClick(e);
+    }
+  }
+
   handleClick = e => {
     const i = Number(e.currentTarget.getAttribute('data-index'));
     const { action, to } = this.props.items[i];
@@ -120,7 +126,7 @@ class DropdownMenu extends React.PureComponent {
 
     return (
       <li className='dropdown-menu__item' key={`${text}-${i}`}>
-        <a href={href} target='_blank' rel='noopener' role='button' tabIndex='0' ref={i === 0 ? this.setFocusRef : null} onClick={this.handleClick} onKeyDown={this.handleKeyDown} data-index={i}>
+        <a href={href} target='_blank' rel='noopener' role='button' tabIndex='0' ref={i === 0 ? this.setFocusRef : null} onClick={this.handleClick} onKeyDown={this.handleItemKeyDown} data-index={i}>
           {text}
         </a>
       </li>
@@ -170,6 +176,7 @@ export default class Dropdown extends React.PureComponent {
     onClose: PropTypes.func.isRequired,
     dropdownPlacement: PropTypes.string,
     openDropdownId: PropTypes.number,
+    openedViaKeyboard: PropTypes.bool,
   };
 
   static defaultProps = {
@@ -180,14 +187,14 @@ export default class Dropdown extends React.PureComponent {
     id: id++,
   };
 
-  handleClick = ({ target }) => {
+  handleClick = ({ target, type }) => {
     if (this.state.id === this.props.openDropdownId) {
       this.handleClose();
     } else {
       const { top } = target.getBoundingClientRect();
       const placement = top * 2 < innerHeight ? 'bottom' : 'top';
 
-      this.props.onOpen(this.state.id, this.handleItemClick, placement);
+      this.props.onOpen(this.state.id, this.handleItemClick, placement, type !== 'click');
     }
   }
 
@@ -197,6 +204,11 @@ export default class Dropdown extends React.PureComponent {
 
   handleKeyDown = e => {
     switch(e.key) {
+    case ' ':
+    case 'Enter':
+      this.handleClick(e);
+      e.preventDefault();
+      break;
     case 'Escape':
       this.handleClose();
       break;
@@ -232,7 +244,7 @@ export default class Dropdown extends React.PureComponent {
   }
 
   render () {
-    const { icon, items, size, ariaLabel, disabled, dropdownPlacement, openDropdownId } = this.props;
+    const { icon, items, size, ariaLabel, disabled, dropdownPlacement, openDropdownId, openedViaKeyboard } = this.props;
     const open = this.state.id === openDropdownId;
 
     return (
@@ -248,7 +260,7 @@ export default class Dropdown extends React.PureComponent {
         />
 
         <Overlay show={open} placement={dropdownPlacement} target={this.findTarget}>
-          <DropdownMenu items={items} onClose={this.handleClose} />
+          <DropdownMenu items={items} onClose={this.handleClose} openedViaKeyboard={openedViaKeyboard} />
         </Overlay>
       </div>
     );
diff --git a/app/javascript/flavours/glitch/components/media_gallery.js b/app/javascript/flavours/glitch/components/media_gallery.js
index 3faf0b453..605a2862b 100644
--- a/app/javascript/flavours/glitch/components/media_gallery.js
+++ b/app/javascript/flavours/glitch/components/media_gallery.js
@@ -70,7 +70,7 @@ class Item extends React.PureComponent {
   handleClick = (e) => {
     const { index, onClick } = this.props;
 
-    if (e.button === 0) {
+    if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
       e.preventDefault();
       onClick(index);
     }
@@ -78,6 +78,11 @@ class Item extends React.PureComponent {
     e.stopPropagation();
   }
 
+  handleMouseDown = (e) => {
+    e.preventDefault();
+    e.stopPropagation();
+  }
+
   render () {
     const { attachment, index, size, standalone, letterbox, displayWidth } = this.props;
 
@@ -163,7 +168,8 @@ class Item extends React.PureComponent {
             sizes={sizes}
             alt={attachment.get('description')}
             title={attachment.get('description')}
-            style={{ objectPosition: `${x}% ${y}%` }} />
+            style={{ objectPosition: letterbox ? null : `${x}% ${y}%` }}
+          />
         </a>
       );
     } else if (attachment.get('type') === 'gifv') {
@@ -180,6 +186,7 @@ class Item extends React.PureComponent {
             onClick={this.handleClick}
             onMouseEnter={this.handleMouseEnter}
             onMouseLeave={this.handleMouseLeave}
+            onMouseDown={this.handleMouseDown}
             autoPlay={autoPlay}
             loop
             muted
@@ -191,7 +198,7 @@ class Item extends React.PureComponent {
     }
 
     return (
-      <div className={classNames('media-gallery__item', { standalone })} key={attachment.get('id')} style={{ left: left, top: top, right: right, bottom: bottom, width: `${width}%`, height: `${height}%` }}>
+      <div className={classNames('media-gallery__item', { standalone, letterbox })} key={attachment.get('id')} style={{ left: left, top: top, right: right, bottom: bottom, width: `${width}%`, height: `${height}%` }}>
         {thumbnail}
       </div>
     );
@@ -261,6 +268,8 @@ export default class MediaGallery extends React.PureComponent {
 
     if (this.isStandaloneEligible() && width) {
       style.height = width / this.props.media.getIn([0, 'meta', 'small', 'aspect']);
+    } else if (width) {
+      style.height = width / (16/9);
     }
 
     if (!visible) {
@@ -280,7 +289,7 @@ export default class MediaGallery extends React.PureComponent {
       }
     }
 
-    const computedClass = classNames('media-gallery', `size-${size}`, { 'full-width': fullwidth });
+    const computedClass = classNames('media-gallery', { 'full-width': fullwidth });
 
     return (
       <div className={computedClass} style={style} ref={this.handleRef}>
diff --git a/app/javascript/flavours/glitch/components/scrollable_list.js b/app/javascript/flavours/glitch/components/scrollable_list.js
index b96b4dd98..a677cbf5b 100644
--- a/app/javascript/flavours/glitch/components/scrollable_list.js
+++ b/app/javascript/flavours/glitch/components/scrollable_list.js
@@ -149,6 +149,10 @@ export default class ScrollableList extends PureComponent {
     this.props.onLoadMore();
   }
 
+  defaultShouldUpdateScroll = (prevRouterProps, { location }) => {
+    return !(location.state && location.state.mastodonModalOpen);
+  }
+
   render () {
     const { children, scrollKey, trackScroll, shouldUpdateScroll, isLoading, hasMore, prepend, emptyMessage, onLoadMore } = this.props;
     const { fullscreen } = this.state;
@@ -190,7 +194,7 @@ export default class ScrollableList extends PureComponent {
 
     if (trackScroll) {
       return (
-        <ScrollContainer scrollKey={scrollKey} shouldUpdateScroll={shouldUpdateScroll}>
+        <ScrollContainer scrollKey={scrollKey} shouldUpdateScroll={shouldUpdateScroll || this.defaultShouldUpdateScroll}>
           {scrollableArea}
         </ScrollContainer>
       );
diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js
index 13ff781fe..4d47660c2 100644
--- a/app/javascript/flavours/glitch/components/status.js
+++ b/app/javascript/flavours/glitch/components/status.js
@@ -450,6 +450,7 @@ export default class Status extends ImmutablePureComponent {
               sensitive={status.get('sensitive')}
               letterbox={settings.getIn(['media', 'letterbox'])}
               fullwidth={settings.getIn(['media', 'fullwidth'])}
+              preventPlayback={isCollapsed || !isExpanded}
               onOpenVideo={this.handleOpenVideo}
             />)}
           </Bundle>
diff --git a/app/javascript/flavours/glitch/components/status_action_bar.js b/app/javascript/flavours/glitch/components/status_action_bar.js
index e26bdb717..f7e741d2d 100644
--- a/app/javascript/flavours/glitch/components/status_action_bar.js
+++ b/app/javascript/flavours/glitch/components/status_action_bar.js
@@ -5,7 +5,7 @@ import IconButton from './icon_button';
 import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
 import { defineMessages, injectIntl } from 'react-intl';
 import ImmutablePureComponent from 'react-immutable-pure-component';
-import { me } from 'flavours/glitch/util/initial_state';
+import { me, isStaff } from 'flavours/glitch/util/initial_state';
 import RelativeTimestamp from './relative_timestamp';
 
 const messages = defineMessages({
@@ -31,6 +31,8 @@ const messages = defineMessages({
   pin: { id: 'status.pin', defaultMessage: 'Pin on profile' },
   unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
   embed: { id: 'status.embed', defaultMessage: 'Embed' },
+  admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
+  admin_status: { id: 'status.admin_status', defaultMessage: 'Open this status in the moderation interface' },
 });
 
 const obfuscatedCount = count => {
@@ -186,6 +188,11 @@ export default class StatusActionBar extends ImmutablePureComponent {
       menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick });
       menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
       menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
+      if (isStaff) {
+        menu.push(null);
+        menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
+        menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` });
+      }
     }
 
     if (status.get('in_reply_to_id', null) === null) {
diff --git a/app/javascript/flavours/glitch/containers/dropdown_menu_container.js b/app/javascript/flavours/glitch/containers/dropdown_menu_container.js
index dccd93dab..b2419a0fd 100644
--- a/app/javascript/flavours/glitch/containers/dropdown_menu_container.js
+++ b/app/javascript/flavours/glitch/containers/dropdown_menu_container.js
@@ -8,20 +8,21 @@ const mapStateToProps = state => ({
   isModalOpen: state.get('modal').modalType === 'ACTIONS',
   dropdownPlacement: state.getIn(['dropdown_menu', 'placement']),
   openDropdownId: state.getIn(['dropdown_menu', 'openId']),
+  openedViaKeyboard: state.getIn(['dropdown_menu', 'keyboard']),
 });
 
 const mapDispatchToProps = (dispatch, { status, items }) => ({
-  onOpen(id, onItemClick, dropdownPlacement) {
+  onOpen(id, onItemClick, dropdownPlacement, keyboard) {
     dispatch(isUserTouching() ? openModal('ACTIONS', {
       status,
       actions: items.map(
         (item, i) => item ? {
           ...item,
           name: `${item.text}-${i}`,
-          onClick: (e) => { return onItemClick(i, e) },
+          onClick: item.action ? ((e) => { return onItemClick(i, e) }) : null,
         } : null
       ),
-    }) : openDropdownMenu(id, dropdownPlacement));
+    }) : openDropdownMenu(id, dropdownPlacement, keyboard));
   },
   onClose(id) {
     dispatch(closeModal());
diff --git a/app/javascript/flavours/glitch/features/account/components/action_bar.js b/app/javascript/flavours/glitch/features/account/components/action_bar.js
index 9c80a470b..3d6eeb06a 100644
--- a/app/javascript/flavours/glitch/features/account/components/action_bar.js
+++ b/app/javascript/flavours/glitch/features/account/components/action_bar.js
@@ -2,9 +2,9 @@ import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
-import { Link } from 'react-router-dom';
+import { NavLink } from 'react-router-dom';
 import { defineMessages, injectIntl, FormattedMessage, FormattedNumber } from 'react-intl';
-import { me } from 'flavours/glitch/util/initial_state';
+import { me, isStaff } from 'flavours/glitch/util/initial_state';
 
 const messages = defineMessages({
   mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' },
@@ -25,6 +25,7 @@ const messages = defineMessages({
   showReblogs: { id: 'account.show_reblogs', defaultMessage: 'Show boosts from @{name}' },
   endorse: { id: 'account.endorse', defaultMessage: 'Feature on profile' },
   unendorse: { id: 'account.unendorse', defaultMessage: 'Don\'t feature on profile' },
+  admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
 });
 
 @injectIntl
@@ -51,6 +52,13 @@ export default class ActionBar extends React.PureComponent {
     });
   }
 
+  isStatusesPageActive = (match, location) => {
+    if (!match) {
+      return false;
+    }
+    return !location.pathname.match(/\/(followers|following)\/?$/);
+  }
+
   render () {
     const { account, intl } = this.props;
 
@@ -120,6 +128,11 @@ export default class ActionBar extends React.PureComponent {
       }
     }
 
+    if (account.get('id') !== me && isStaff) {
+      menu.push(null);
+      menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: `/admin/accounts/${account.get('id')}` });
+    }
+
     return (
       <div>
         {extraInfo}
@@ -130,20 +143,20 @@ export default class ActionBar extends React.PureComponent {
           </div>
 
           <div className='account__action-bar-links'>
-            <Link className='account__action-bar__tab' to={`/accounts/${account.get('id')}`}>
+            <NavLink isActive={this.isStatusesPageActive} activeClassName='active' className='account__action-bar__tab' to={`/accounts/${account.get('id')}`}>
               <FormattedMessage id='account.posts' defaultMessage='Posts' />
               <strong><FormattedNumber value={account.get('statuses_count')} /></strong>
-            </Link>
+            </NavLink>
 
-            <Link className='account__action-bar__tab' to={`/accounts/${account.get('id')}/following`}>
+            <NavLink exact activeClassName='active' className='account__action-bar__tab' to={`/accounts/${account.get('id')}/following`}>
               <FormattedMessage id='account.follows' defaultMessage='Follows' />
               <strong><FormattedNumber value={account.get('following_count')} /></strong>
-            </Link>
+            </NavLink>
 
-            <Link className='account__action-bar__tab' to={`/accounts/${account.get('id')}/followers`}>
+            <NavLink exact activeClassName='active' className='account__action-bar__tab' to={`/accounts/${account.get('id')}/followers`}>
               <FormattedMessage id='account.followers' defaultMessage='Followers' />
               <strong><FormattedNumber value={account.get('followers_count')} /></strong>
-            </Link>
+            </NavLink>
           </div>
         </div>
       </div>
diff --git a/app/javascript/flavours/glitch/features/account/components/header.js b/app/javascript/flavours/glitch/features/account/components/header.js
index eda0d637e..f0d36947d 100644
--- a/app/javascript/flavours/glitch/features/account/components/header.js
+++ b/app/javascript/flavours/glitch/features/account/components/header.js
@@ -15,8 +15,19 @@ const messages = defineMessages({
   follow: { id: 'account.follow', defaultMessage: 'Follow' },
   requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' },
   unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
+  edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
+  link_verified_on: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' },
 });
 
+const dateFormatOptions = {
+  month: 'short',
+  day: 'numeric',
+  year: 'numeric',
+  hour12: false,
+  hour: '2-digit',
+  minute: '2-digit',
+};
+
 @injectIntl
 export default class Header extends ImmutablePureComponent {
 
@@ -27,6 +38,10 @@ export default class Header extends ImmutablePureComponent {
     intl: PropTypes.object.isRequired,
   };
 
+  openEditProfile = () => {
+    window.open('/settings/profile', '_blank');
+  }
+
   render () {
     const { account, intl } = this.props;
 
@@ -77,6 +92,12 @@ export default class Header extends ImmutablePureComponent {
           </div>
         );
       }
+    } else {
+      actionBtn = (
+        <div className='account--action-button'>
+          <IconButton size={26} icon='pencil' title={intl.formatMessage(messages.edit_profile)} onClick={this.openEditProfile} />
+        </div>
+      );
     }
 
     if (account.get('moved') && !account.getIn(['relationship', 'following'])) {
@@ -111,7 +132,9 @@ export default class Header extends ImmutablePureComponent {
                 {fields.map((pair, i) => (
                   <dl key={i}>
                     <dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} />
-                    <dd dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} title={pair.get('value_plain')} />
+                    <dd className={pair.get('verified_at') && 'verified'} title={pair.get('value_plain')}>
+                      {pair.get('verified_at') && <span title={intl.formatMessage(messages.link_verified_on, { date: intl.formatDate(pair.get('verified_at'), dateFormatOptions) })}><i className='fa fa-check verified__mark' /></span>} <span dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} />
+                    </dd>
                  </dl>
                 ))}
               </div>
diff --git a/app/javascript/flavours/glitch/features/account_timeline/index.js b/app/javascript/flavours/glitch/features/account_timeline/index.js
index 20ba0a1b1..2216f9153 100644
--- a/app/javascript/flavours/glitch/features/account_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/account_timeline/index.js
@@ -60,10 +60,6 @@ export default class AccountTimeline extends ImmutablePureComponent {
     this.props.dispatch(expandAccountTimeline(this.props.params.accountId, { maxId, withReplies: this.props.withReplies }));
   }
 
-  shouldUpdateScroll = (prevRouterProps, { location }) => {
-    return !(location.state && location.state.mastodonModalOpen)
-  }
-
   render () {
     const { statusIds, featuredStatusIds, isLoading, hasMore } = this.props;
 
@@ -87,7 +83,6 @@ export default class AccountTimeline extends ImmutablePureComponent {
           isLoading={isLoading}
           hasMore={hasMore}
           onLoadMore={this.handleLoadMore}
-          shouldUpdateScroll={this.shouldUpdateScroll}
         />
       </Column>
     );
diff --git a/app/javascript/flavours/glitch/features/bookmarked_statuses/index.js b/app/javascript/flavours/glitch/features/bookmarked_statuses/index.js
index f1b4f947e..9468ad81d 100644
--- a/app/javascript/flavours/glitch/features/bookmarked_statuses/index.js
+++ b/app/javascript/flavours/glitch/features/bookmarked_statuses/index.js
@@ -66,10 +66,6 @@ export default class Bookmarks extends ImmutablePureComponent {
     this.props.dispatch(expandBookmarkedStatuses());
   }, 300, { leading: true })
 
-  shouldUpdateScroll = (prevRouterProps, { location }) => {
-    return !(location.state && location.state.mastodonModalOpen)
-  }
-
   render () {
     const { intl, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
     const pinned = !!columnId;
@@ -91,7 +87,6 @@ export default class Bookmarks extends ImmutablePureComponent {
           trackScroll={!pinned}
           statusIds={statusIds}
           scrollKey={`bookmarked_statuses-${columnId}`}
-          shouldUpdateScroll={this.shouldUpdateScroll}
           hasMore={hasMore}
           isLoading={isLoading}
           onLoadMore={this.handleLoadMore}
diff --git a/app/javascript/flavours/glitch/features/community_timeline/index.js b/app/javascript/flavours/glitch/features/community_timeline/index.js
index 5bd9d037c..ddcca2dc0 100644
--- a/app/javascript/flavours/glitch/features/community_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/community_timeline/index.js
@@ -71,10 +71,6 @@ export default class CommunityTimeline extends React.PureComponent {
     this.props.dispatch(expandCommunityTimeline({ maxId }));
   }
 
-  shouldUpdateScroll = (prevRouterProps, { location }) => {
-    return !(location.state && location.state.mastodonModalOpen)
-  }
-
   render () {
     const { intl, hasUnread, columnId, multiColumn } = this.props;
     const pinned = !!columnId;
@@ -97,7 +93,6 @@ export default class CommunityTimeline extends React.PureComponent {
         <StatusListContainer
           trackScroll={!pinned}
           scrollKey={`community_timeline-${columnId}`}
-          shouldUpdateScroll={this.shouldUpdateScroll}
           timelineId='community'
           onLoadMore={this.handleLoadMore}
           emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />}
diff --git a/app/javascript/flavours/glitch/features/composer/index.js b/app/javascript/flavours/glitch/features/composer/index.js
index bc409f0a3..257797047 100644
--- a/app/javascript/flavours/glitch/features/composer/index.js
+++ b/app/javascript/flavours/glitch/features/composer/index.js
@@ -102,6 +102,7 @@ function mapStateToProps (state) {
     anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
     spoilersAlwaysOn: spoilersAlwaysOn,
     mediaDescriptionConfirmation: state.getIn(['local_settings', 'confirm_missing_media_description']),
+    preselectOnReply: state.getIn(['local_settings', 'preselect_on_reply']),
   };
 };
 
@@ -146,7 +147,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
   onMount() {
     dispatch(mountCompose());
   },
-  onOpenActionModal(props) {
+  onOpenActionsModal(props) {
     dispatch(openModal('ACTIONS', props));
   },
   onOpenDoodleModal() {
@@ -242,7 +243,7 @@ const handlers = {
     }
 
     // Submit disabled:
-    if (isSubmitting || isUploading || (!!text.length && !text.trim().length && !anyMedia)) {
+    if (isSubmitting || isUploading || (!text.trim().length && !anyMedia)) {
       return;
     }
 
@@ -328,13 +329,14 @@ class Composer extends React.Component {
       isSubmitting,
       preselectDate,
       text,
+      preselectOnReply,
     } = this.props;
     let selectionEnd, selectionStart;
 
     //  Caret/selection handling.
     if (focusDate !== prevProps.focusDate) {
       switch (true) {
-      case preselectDate !== prevProps.preselectDate:
+      case preselectDate !== prevProps.preselectDate && preselectOnReply:
         selectionStart = text.search(/\s/) + 1;
         selectionEnd = text.length;
         break;
@@ -347,6 +349,7 @@ class Composer extends React.Component {
       if (textarea) {
         textarea.setSelectionRange(selectionStart, selectionEnd);
         textarea.focus();
+        textarea.scrollIntoView();
       }
 
     //  Refocuses the textarea after submitting.
@@ -415,7 +418,7 @@ class Composer extends React.Component {
       spoilersAlwaysOn,
     } = this.props;
 
-    let disabledButton = isSubmitting || isUploading || (!!text.length && !text.trim().length && !anyMedia);
+    let disabledButton = isSubmitting || isUploading || (!text.trim().length && !anyMedia);
 
     return (
       <div className='composer'>
@@ -485,8 +488,8 @@ class Composer extends React.Component {
           onUpload={onUpload}
           privacy={privacy}
           resetFileKey={resetFileKey}
-          sensitive={sensitive}
-          spoiler={spoiler}
+          sensitive={sensitive || (spoilersAlwaysOn && spoilerText && spoilerText.length > 0)}
+          spoiler={spoilersAlwaysOn ? (spoilerText && spoilerText.length > 0) : spoiler}
         />
         <ComposerPublisher
           countText={`${spoilerText}${countableText(text)}${advancedOptions && advancedOptions.get('do_not_federate') ? ' 👁️' : ''}`}
@@ -533,6 +536,7 @@ Composer.propTypes = {
   anyMedia: PropTypes.bool,
   spoilersAlwaysOn: PropTypes.bool,
   mediaDescriptionConfirmation: PropTypes.bool,
+  preselectOnReply: PropTypes.bool,
 
   //  Dispatch props.
   onCancelReply: PropTypes.func,
diff --git a/app/javascript/flavours/glitch/features/composer/textarea/suggestions/item/index.js b/app/javascript/flavours/glitch/features/composer/textarea/suggestions/item/index.js
index 331692398..1b7ae8904 100644
--- a/app/javascript/flavours/glitch/features/composer/textarea/suggestions/item/index.js
+++ b/app/javascript/flavours/glitch/features/composer/textarea/suggestions/item/index.js
@@ -11,7 +11,7 @@ import { unicodeMapping } from 'flavours/glitch/util/emoji';
 import { assignHandlers } from 'flavours/glitch/util/react_helpers';
 
 //  Gets our asset host from the environment, if available.
-const assetHost = ((process || {}).env || {}).CDN_HOST || '';
+const assetHost = process.env.CDN_HOST || '';
 
 //  Handlers.
 const handlers = {
diff --git a/app/javascript/flavours/glitch/features/direct_timeline/index.js b/app/javascript/flavours/glitch/features/direct_timeline/index.js
index 949c85dce..dc7e0534d 100644
--- a/app/javascript/flavours/glitch/features/direct_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/direct_timeline/index.js
@@ -71,10 +71,6 @@ export default class DirectTimeline extends React.PureComponent {
     this.props.dispatch(expandDirectTimeline({ maxId }));
   }
 
-  shouldUpdateScroll = (prevRouterProps, { location }) => {
-    return !(location.state && location.state.mastodonModalOpen)
-  }
-
   render () {
     const { intl, hasUnread, columnId, multiColumn } = this.props;
     const pinned = !!columnId;
@@ -97,7 +93,6 @@ export default class DirectTimeline extends React.PureComponent {
         <StatusListContainer
           trackScroll={!pinned}
           scrollKey={`direct_timeline-${columnId}`}
-          shouldUpdateScroll={this.shouldUpdateScroll}
           timelineId='direct'
           onLoadMore={this.handleLoadMore}
           emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any direct messages yet. When you send or receive one, it will show up here." />}
diff --git a/app/javascript/flavours/glitch/features/domain_blocks/index.js b/app/javascript/flavours/glitch/features/domain_blocks/index.js
index 8b023e0bc..3b29e2a26 100644
--- a/app/javascript/flavours/glitch/features/domain_blocks/index.js
+++ b/app/javascript/flavours/glitch/features/domain_blocks/index.js
@@ -40,10 +40,6 @@ export default class Blocks extends ImmutablePureComponent {
     this.props.dispatch(expandDomainBlocks());
   }, 300, { leading: true });
 
-  shouldUpdateScroll = (prevRouterProps, { location }) => {
-    return !(location.state && location.state.mastodonModalOpen)
-  }
-
   render () {
     const { intl, domains } = this.props;
 
@@ -58,7 +54,7 @@ export default class Blocks extends ImmutablePureComponent {
     return (
       <Column icon='minus-circle' heading={intl.formatMessage(messages.heading)}>
         <ColumnBackButtonSlim />
-        <ScrollableList scrollKey='domain_blocks' onLoadMore={this.handleLoadMore} shouldUpdateScroll={this.shouldUpdateScroll}>
+        <ScrollableList scrollKey='domain_blocks' onLoadMore={this.handleLoadMore}>
           {domains.map(domain =>
             <DomainContainer key={domain} domain={domain} />
           )}
diff --git a/app/javascript/flavours/glitch/features/drawer/header/index.js b/app/javascript/flavours/glitch/features/drawer/header/index.js
index deec42435..7fefd32c9 100644
--- a/app/javascript/flavours/glitch/features/drawer/header/index.js
+++ b/app/javascript/flavours/glitch/features/drawer/header/index.js
@@ -46,6 +46,8 @@ const messages = defineMessages({
 //  The component.
 export default function DrawerHeader ({
   columns,
+  unreadNotifications,
+  showNotificationsBadge,
   intl,
   onSettingsClick,
 }) {
@@ -77,7 +79,12 @@ export default function DrawerHeader ({
           aria-label={intl.formatMessage(messages.notifications)}
           title={intl.formatMessage(messages.notifications)}
           to='/notifications'
-        ><Icon icon='bell' /></Link>
+        >
+          <span className='icon-badge-wrapper'>
+            <Icon icon='bell' />
+            { showNotificationsBadge && unreadNotifications > 0 && <div className='icon-badge' />}
+          </span>
+        </Link>
       ))}
       {renderForColumn('COMMUNITY', (
         <Link
@@ -112,6 +119,8 @@ export default function DrawerHeader ({
 //  Props.
 DrawerHeader.propTypes = {
   columns: ImmutablePropTypes.list,
+  unreadNotifications: PropTypes.number,
+  showNotificationsBadge: PropTypes.bool,
   intl: PropTypes.object,
   onSettingsClick: PropTypes.func,
 };
diff --git a/app/javascript/flavours/glitch/features/drawer/index.js b/app/javascript/flavours/glitch/features/drawer/index.js
index 497b7d0e4..038a2513e 100644
--- a/app/javascript/flavours/glitch/features/drawer/index.js
+++ b/app/javascript/flavours/glitch/features/drawer/index.js
@@ -40,6 +40,8 @@ const mapStateToProps = state => ({
   searchHidden: state.getIn(['search', 'hidden']),
   searchValue: state.getIn(['search', 'value']),
   submitted: state.getIn(['search', 'submitted']),
+  unreadNotifications: state.getIn(['notifications', 'unread']),
+  showNotificationsBadge: state.getIn(['local_settings', 'notifications', 'tab_badge']),
 });
 
 //  Dispatch mapping.
@@ -93,6 +95,8 @@ class Drawer extends React.Component {
       searchValue,
       submitted,
       isSearchPage,
+      unreadNotifications,
+      showNotificationsBadge,
     } = this.props;
     const computedClass = classNames('drawer', `mbstobon-${elefriend}`);
 
@@ -102,6 +106,8 @@ class Drawer extends React.Component {
         {multiColumn ? (
           <DrawerHeader
             columns={columns}
+            unreadNotifications={unreadNotifications}
+            showNotificationsBadge={showNotificationsBadge}
             intl={intl}
             onSettingsClick={onOpenSettings}
           />
@@ -145,6 +151,8 @@ Drawer.propTypes = {
   searchHidden: PropTypes.bool,
   searchValue: PropTypes.string,
   submitted: PropTypes.bool,
+  unreadNotifications: PropTypes.number,
+  showNotificationsBadge: PropTypes.bool,
 
   //  Dispatch props.
   onChange: PropTypes.func,
diff --git a/app/javascript/flavours/glitch/features/emoji_picker/index.js b/app/javascript/flavours/glitch/features/emoji_picker/index.js
index d22a50848..a78117971 100644
--- a/app/javascript/flavours/glitch/features/emoji_picker/index.js
+++ b/app/javascript/flavours/glitch/features/emoji_picker/index.js
@@ -340,6 +340,7 @@ class EmojiPickerMenu extends React.PureComponent {
           skin={skinTone}
           showPreview={false}
           backgroundImageFn={backgroundImageFn}
+          autoFocus
           emojiTooltip
         />
 
diff --git a/app/javascript/flavours/glitch/features/favourited_statuses/index.js b/app/javascript/flavours/glitch/features/favourited_statuses/index.js
index 923d4b24f..32bf4e71a 100644
--- a/app/javascript/flavours/glitch/features/favourited_statuses/index.js
+++ b/app/javascript/flavours/glitch/features/favourited_statuses/index.js
@@ -66,10 +66,6 @@ export default class Favourites extends ImmutablePureComponent {
     this.props.dispatch(expandFavouritedStatuses());
   }, 300, { leading: true })
 
-  shouldUpdateScroll = (prevRouterProps, { location }) => {
-    return !(location.state && location.state.mastodonModalOpen)
-  }
-
   render () {
     const { intl, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
     const pinned = !!columnId;
@@ -91,7 +87,6 @@ export default class Favourites extends ImmutablePureComponent {
           trackScroll={!pinned}
           statusIds={statusIds}
           scrollKey={`favourited_statuses-${columnId}`}
-          shouldUpdateScroll={this.shouldUpdateScroll}
           hasMore={hasMore}
           isLoading={isLoading}
           onLoadMore={this.handleLoadMore}
diff --git a/app/javascript/flavours/glitch/features/favourites/index.js b/app/javascript/flavours/glitch/features/favourites/index.js
index 055a15ccb..cf8b31eb3 100644
--- a/app/javascript/flavours/glitch/features/favourites/index.js
+++ b/app/javascript/flavours/glitch/features/favourites/index.js
@@ -33,6 +33,10 @@ export default class Favourites extends ImmutablePureComponent {
     }
   }
 
+  shouldUpdateScroll = (prevRouterProps, { location }) => {
+    return !(location.state && location.state.mastodonModalOpen);
+  }
+
   render () {
     const { accountIds } = this.props;
 
@@ -48,7 +52,7 @@ export default class Favourites extends ImmutablePureComponent {
       <Column>
         <ColumnBackButton />
 
-        <ScrollContainer scrollKey='favourites'>
+        <ScrollContainer scrollKey='favourites' shouldUpdateScroll={this.shouldUpdateScroll}>
           <div className='scrollable'>
             {accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} />)}
           </div>
diff --git a/app/javascript/flavours/glitch/features/follow_requests/index.js b/app/javascript/flavours/glitch/features/follow_requests/index.js
index 04ff3f111..1e4633984 100644
--- a/app/javascript/flavours/glitch/features/follow_requests/index.js
+++ b/app/javascript/flavours/glitch/features/follow_requests/index.js
@@ -42,6 +42,10 @@ export default class FollowRequests extends ImmutablePureComponent {
     }
   }
 
+  shouldUpdateScroll = (prevRouterProps, { location }) => {
+    return !(location.state && location.state.mastodonModalOpen);
+  }
+
   render () {
     const { intl, accountIds } = this.props;
 
@@ -57,7 +61,7 @@ export default class FollowRequests extends ImmutablePureComponent {
       <Column name='follow-requests' icon='users' heading={intl.formatMessage(messages.heading)}>
         <ColumnBackButtonSlim />
 
-        <ScrollContainer scrollKey='follow_requests'>
+        <ScrollContainer scrollKey='follow_requests' shouldUpdateScroll={this.shouldUpdateScroll}>
           <div className='scrollable' onScroll={this.handleScroll}>
             {accountIds.map(id =>
               <AccountAuthorizeContainer key={id} id={id} />
diff --git a/app/javascript/flavours/glitch/features/followers/index.js b/app/javascript/flavours/glitch/features/followers/index.js
index c42e0386c..cdde1775c 100644
--- a/app/javascript/flavours/glitch/features/followers/index.js
+++ b/app/javascript/flavours/glitch/features/followers/index.js
@@ -56,6 +56,10 @@ export default class Followers extends ImmutablePureComponent {
     this.props.dispatch(expandFollowers(this.props.params.accountId));
   }
 
+  shouldUpdateScroll = (prevRouterProps, { location }) => {
+    return !(location.state && location.state.mastodonModalOpen);
+  }
+
   render () {
     const { accountIds, hasMore } = this.props;
 
@@ -77,7 +81,7 @@ export default class Followers extends ImmutablePureComponent {
       <Column>
         <ColumnBackButton />
 
-        <ScrollContainer scrollKey='followers'>
+        <ScrollContainer scrollKey='followers' shouldUpdateScroll={this.shouldUpdateScroll}>
           <div className='scrollable' onScroll={this.handleScroll}>
             <div className='followers'>
               <HeaderContainer accountId={this.props.params.accountId} hideTabs />
diff --git a/app/javascript/flavours/glitch/features/hashtag_timeline/index.js b/app/javascript/flavours/glitch/features/hashtag_timeline/index.js
index f710456d5..311fabb63 100644
--- a/app/javascript/flavours/glitch/features/hashtag_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/hashtag_timeline/index.js
@@ -82,10 +82,6 @@ export default class HashtagTimeline extends React.PureComponent {
     this.props.dispatch(expandHashtagTimeline(this.props.params.id, { maxId }));
   }
 
-  shouldUpdateScroll = (prevRouterProps, { location }) => {
-    return !(location.state && location.state.mastodonModalOpen)
-  }
-
   render () {
     const { hasUnread, columnId, multiColumn } = this.props;
     const { id } = this.props.params;
@@ -110,7 +106,6 @@ export default class HashtagTimeline extends React.PureComponent {
           scrollKey={`hashtag_timeline-${columnId}`}
           timelineId={`hashtag:${id}`}
           onLoadMore={this.handleLoadMore}
-          shouldUpdateScroll={this.shouldUpdateScroll}
           emptyMessage={<FormattedMessage id='empty_column.hashtag' defaultMessage='There is nothing in this hashtag yet.' />}
         />
       </Column>
diff --git a/app/javascript/flavours/glitch/features/local_settings/navigation/index.js b/app/javascript/flavours/glitch/features/local_settings/navigation/index.js
index 0c1040290..a992b1ffc 100644
--- a/app/javascript/flavours/glitch/features/local_settings/navigation/index.js
+++ b/app/javascript/flavours/glitch/features/local_settings/navigation/index.js
@@ -10,6 +10,7 @@ import LocalSettingsNavigationItem from './item';
 
 const messages = defineMessages({
   general: {  id: 'settings.general', defaultMessage: 'General' },
+  compose: {  id: 'settings.compose_box_opts', defaultMessage: 'Compose box options' },
   content_warnings: { id: 'settings.content_warnings', defaultMessage: 'Content Warnings' },
   collapsed: { id: 'settings.collapsed_statuses', defaultMessage: 'Collapsed toots' },
   media: { id: 'settings.media', defaultMessage: 'Media' },
@@ -43,31 +44,37 @@ export default class LocalSettingsNavigation extends React.PureComponent {
           active={index === 1}
           index={1}
           onNavigate={onNavigate}
-          title={intl.formatMessage(messages.content_warnings)}
+          title={intl.formatMessage(messages.compose)}
         />
         <LocalSettingsNavigationItem
           active={index === 2}
           index={2}
           onNavigate={onNavigate}
-          title={intl.formatMessage(messages.collapsed)}
+          title={intl.formatMessage(messages.content_warnings)}
         />
         <LocalSettingsNavigationItem
           active={index === 3}
           index={3}
           onNavigate={onNavigate}
-          title={intl.formatMessage(messages.media)}
+          title={intl.formatMessage(messages.collapsed)}
         />
         <LocalSettingsNavigationItem
           active={index === 4}
-          href='/settings/preferences'
           index={4}
+          onNavigate={onNavigate}
+          title={intl.formatMessage(messages.media)}
+        />
+        <LocalSettingsNavigationItem
+          active={index === 5}
+          href='/settings/preferences'
+          index={5}
           icon='cog'
           title={intl.formatMessage(messages.preferences)}
         />
         <LocalSettingsNavigationItem
-          active={index === 5}
+          active={index === 6}
           className='close'
-          index={5}
+          index={6}
           onNavigate={onClose}
           title={intl.formatMessage(messages.close)}
         />
diff --git a/app/javascript/flavours/glitch/features/local_settings/page/index.js b/app/javascript/flavours/glitch/features/local_settings/page/index.js
index 0db49ba5d..5b4e0cb01 100644
--- a/app/javascript/flavours/glitch/features/local_settings/page/index.js
+++ b/app/javascript/flavours/glitch/features/local_settings/page/index.js
@@ -43,6 +43,25 @@ export default class LocalSettingsPage extends React.PureComponent {
           <FormattedMessage id='settings.show_reply_counter' defaultMessage='Display an estimate of the reply count' />
         </LocalSettingsPageItem>
         <section>
+          <h2><FormattedMessage id='settings.notifications_opts' defaultMessage='Notifications options' /></h2>
+          <LocalSettingsPageItem
+            settings={settings}
+            item={['notifications', 'tab_badge']}
+            id='mastodon-settings--notifications-tab_badge'
+            onChange={onChange}
+          >
+            <FormattedMessage id='settings.notifications.tab_badge' defaultMessage="Display a badge for unread notifications if the notifications column isn't open" />
+          </LocalSettingsPageItem>
+          <LocalSettingsPageItem
+            settings={settings}
+            item={['notifications', 'favicon_badge']}
+            id='mastodon-settings--notifications-favicon_badge'
+            onChange={onChange}
+          >
+            <FormattedMessage id='settings.notifications.favicon_badge' defaultMessage='Display unread notifications count in the favicon' />
+          </LocalSettingsPageItem>
+        </section>
+        <section>
           <h2><FormattedMessage id='settings.layout_opts' defaultMessage='Layout options' /></h2>
           <LocalSettingsPageItem
             settings={settings}
@@ -74,53 +93,63 @@ export default class LocalSettingsPage extends React.PureComponent {
             <FormattedMessage id='settings.navbar_under' defaultMessage='Navbar at the bottom (Mobile only)' />
           </LocalSettingsPageItem>
         </section>
-        <section>
-          <h2><FormattedMessage id='settings.compose_box_opts' defaultMessage='Compose box options' /></h2>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['always_show_spoilers_field']}
-            id='mastodon-settings--always_show_spoilers_field'
-            onChange={onChange}
-          >
-            <FormattedMessage id='settings.always_show_spoilers_field' defaultMessage='Always enable the Content Warning field' />
-          </LocalSettingsPageItem>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['confirm_missing_media_description']}
-            id='mastodon-settings--confirm_missing_media_description'
-            onChange={onChange}
-          >
-            <FormattedMessage id='settings.confirm_missing_media_description' defaultMessage='Show confirmation dialog before sending toots lacking media descriptions' />
-          </LocalSettingsPageItem>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['side_arm']}
-            id='mastodon-settings--side_arm'
-            options={[
-              { value: 'none', message: intl.formatMessage(messages.side_arm_none) },
-              { value: 'direct', message: intl.formatMessage({ id: 'privacy.direct.short' }) },
-              { value: 'private', message: intl.formatMessage({ id: 'privacy.private.short' }) },
-              { value: 'unlisted', message: intl.formatMessage({ id: 'privacy.unlisted.short' }) },
-              { value: 'public', message: intl.formatMessage({ id: 'privacy.public.short' }) },
-            ]}
-            onChange={onChange}
-          >
-            <FormattedMessage id='settings.side_arm' defaultMessage='Secondary toot button:' />
-          </LocalSettingsPageItem>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['side_arm_reply_mode']}
-            id='mastodon-settings--side_arm_reply_mode'
-            options={[
-              { value: 'keep', message: intl.formatMessage(messages.side_arm_keep) },
-              { value: 'copy', message: intl.formatMessage(messages.side_arm_copy) },
-              { value: 'restrict', message: intl.formatMessage(messages.side_arm_restrict) },
-            ]}
-            onChange={onChange}
-          >
-            <FormattedMessage id='settings.side_arm_reply_mode' defaultMessage='When replying to a toot:' />
-          </LocalSettingsPageItem>
-        </section>
+      </div>
+    ),
+    ({ intl, onChange, settings }) => (
+      <div className='glitch local-settings__page compose_box_opts'>
+        <h1><FormattedMessage id='settings.compose_box_opts' defaultMessage='Compose box options' /></h1>
+        <LocalSettingsPageItem
+          settings={settings}
+          item={['always_show_spoilers_field']}
+          id='mastodon-settings--always_show_spoilers_field'
+          onChange={onChange}
+        >
+          <FormattedMessage id='settings.always_show_spoilers_field' defaultMessage='Always enable the Content Warning field' />
+        </LocalSettingsPageItem>
+        <LocalSettingsPageItem
+          settings={settings}
+          item={['preselect_on_reply']}
+          id='mastodon-settings--preselect_on_reply'
+          onChange={onChange}
+        >
+          <FormattedMessage id='settings.preselect_on_reply' defaultMessage='Pre-select usernames past the first when replying to a toot with multiple participants' />
+        </LocalSettingsPageItem>
+        <LocalSettingsPageItem
+          settings={settings}
+          item={['confirm_missing_media_description']}
+          id='mastodon-settings--confirm_missing_media_description'
+          onChange={onChange}
+        >
+          <FormattedMessage id='settings.confirm_missing_media_description' defaultMessage='Show confirmation dialog before sending toots lacking media descriptions' />
+        </LocalSettingsPageItem>
+        <LocalSettingsPageItem
+          settings={settings}
+          item={['side_arm']}
+          id='mastodon-settings--side_arm'
+          options={[
+            { value: 'none', message: intl.formatMessage(messages.side_arm_none) },
+            { value: 'direct', message: intl.formatMessage({ id: 'privacy.direct.short' }) },
+            { value: 'private', message: intl.formatMessage({ id: 'privacy.private.short' }) },
+            { value: 'unlisted', message: intl.formatMessage({ id: 'privacy.unlisted.short' }) },
+            { value: 'public', message: intl.formatMessage({ id: 'privacy.public.short' }) },
+          ]}
+          onChange={onChange}
+        >
+          <FormattedMessage id='settings.side_arm' defaultMessage='Secondary toot button:' />
+        </LocalSettingsPageItem>
+        <LocalSettingsPageItem
+          settings={settings}
+          item={['side_arm_reply_mode']}
+          id='mastodon-settings--side_arm_reply_mode'
+          options={[
+            { value: 'keep', message: intl.formatMessage(messages.side_arm_keep) },
+            { value: 'copy', message: intl.formatMessage(messages.side_arm_copy) },
+            { value: 'restrict', message: intl.formatMessage(messages.side_arm_restrict) },
+          ]}
+          onChange={onChange}
+        >
+          <FormattedMessage id='settings.side_arm_reply_mode' defaultMessage='When replying to a toot:' />
+        </LocalSettingsPageItem>
       </div>
     ),
     ({ intl, onChange, settings }) => (
diff --git a/app/javascript/flavours/glitch/features/mutes/index.js b/app/javascript/flavours/glitch/features/mutes/index.js
index 87517eec9..d94c1d8ad 100644
--- a/app/javascript/flavours/glitch/features/mutes/index.js
+++ b/app/javascript/flavours/glitch/features/mutes/index.js
@@ -42,6 +42,10 @@ export default class Mutes extends ImmutablePureComponent {
     }
   }
 
+  shouldUpdateScroll = (prevRouterProps, { location }) => {
+    return !(location.state && location.state.mastodonModalOpen);
+  }
+
   render () {
     const { intl, accountIds } = this.props;
 
@@ -56,7 +60,7 @@ export default class Mutes extends ImmutablePureComponent {
     return (
       <Column name='mutes' icon='volume-off' heading={intl.formatMessage(messages.heading)}>
         <ColumnBackButtonSlim />
-        <ScrollContainer scrollKey='mutes'>
+        <ScrollContainer scrollKey='mutes' shouldUpdateScroll={this.shouldUpdateScroll}>
           <div className='scrollable mutes' onScroll={this.handleScroll}>
             {accountIds.map(id =>
               <AccountContainer key={id} id={id} />
diff --git a/app/javascript/flavours/glitch/features/notifications/index.js b/app/javascript/flavours/glitch/features/notifications/index.js
index 4c05b24fe..13ed26865 100644
--- a/app/javascript/flavours/glitch/features/notifications/index.js
+++ b/app/javascript/flavours/glitch/features/notifications/index.js
@@ -8,6 +8,8 @@ import {
   enterNotificationClearingMode,
   expandNotifications,
   scrollTopNotifications,
+  mountNotifications,
+  unmountNotifications,
 } from 'flavours/glitch/actions/notifications';
 import { addColumn, removeColumn, moveColumn } from 'flavours/glitch/actions/columns';
 import NotificationContainer from './containers/notification_container';
@@ -42,6 +44,12 @@ const mapDispatchToProps = dispatch => ({
   onEnterCleaningMode(yes) {
     dispatch(enterNotificationClearingMode(yes));
   },
+  onMount() {
+    dispatch(mountNotifications());
+  },
+  onUnmount() {
+    dispatch(unmountNotifications());
+  },
   dispatch,
 });
 
@@ -62,6 +70,8 @@ export default class Notifications extends React.PureComponent {
     localSettings: ImmutablePropTypes.map,
     notifCleaningActive: PropTypes.bool,
     onEnterCleaningMode: PropTypes.func,
+    onMount: PropTypes.func,
+    onUnmount: PropTypes.func,
   };
 
   static defaultProps = {
@@ -126,6 +136,20 @@ export default class Notifications extends React.PureComponent {
     }
   }
 
+  componentDidMount () {
+    const { onMount } = this.props;
+    if (onMount) {
+      onMount();
+    }
+  }
+
+  componentWillUnmount () {
+    const { onUnmount } = this.props;
+    if (onUnmount) {
+      onUnmount();
+    }
+  }
+
   render () {
     const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore } = this.props;
     const pinned = !!columnId;
diff --git a/app/javascript/flavours/glitch/features/pinned_statuses/index.js b/app/javascript/flavours/glitch/features/pinned_statuses/index.js
index e7fa7ac0d..f56d70176 100644
--- a/app/javascript/flavours/glitch/features/pinned_statuses/index.js
+++ b/app/javascript/flavours/glitch/features/pinned_statuses/index.js
@@ -41,10 +41,6 @@ export default class PinnedStatuses extends ImmutablePureComponent {
     this.column = c;
   }
 
-  shouldUpdateScroll = (prevRouterProps, { location }) => {
-    return !(location.state && location.state.mastodonModalOpen)
-  }
-
   render () {
     const { intl, statusIds, hasMore } = this.props;
 
@@ -54,7 +50,6 @@ export default class PinnedStatuses extends ImmutablePureComponent {
         <StatusList
           statusIds={statusIds}
           scrollKey='pinned_statuses'
-          shouldUpdateScroll={this.shouldUpdateScroll}
           hasMore={hasMore}
         />
       </Column>
diff --git a/app/javascript/flavours/glitch/features/public_timeline/index.js b/app/javascript/flavours/glitch/features/public_timeline/index.js
index af86b22ac..53f2836f1 100644
--- a/app/javascript/flavours/glitch/features/public_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/public_timeline/index.js
@@ -71,10 +71,6 @@ export default class PublicTimeline extends React.PureComponent {
     this.props.dispatch(expandPublicTimeline({ maxId }));
   }
 
-  shouldUpdateScroll = (prevRouterProps, { location }) => {
-    return !(location.state && location.state.mastodonModalOpen)
-  }
-
   render () {
     const { intl, columnId, hasUnread, multiColumn } = this.props;
     const pinned = !!columnId;
@@ -99,7 +95,6 @@ export default class PublicTimeline extends React.PureComponent {
           onLoadMore={this.handleLoadMore}
           trackScroll={!pinned}
           scrollKey={`public_timeline-${columnId}`}
-          shouldUpdateScroll={this.shouldUpdateScroll}
           emptyMessage={<FormattedMessage id='empty_column.public' defaultMessage='There is nothing here! Write something publicly, or manually follow users from other instances to fill it up' />}
         />
       </Column>
diff --git a/app/javascript/flavours/glitch/features/reblogs/index.js b/app/javascript/flavours/glitch/features/reblogs/index.js
index 25b792b39..c0a65d1de 100644
--- a/app/javascript/flavours/glitch/features/reblogs/index.js
+++ b/app/javascript/flavours/glitch/features/reblogs/index.js
@@ -33,6 +33,10 @@ export default class Reblogs extends ImmutablePureComponent {
     }
   }
 
+  shouldUpdateScroll = (prevRouterProps, { location }) => {
+    return !(location.state && location.state.mastodonModalOpen);
+  }
+
   render () {
     const { accountIds } = this.props;
 
@@ -48,7 +52,7 @@ export default class Reblogs extends ImmutablePureComponent {
       <Column>
         <ColumnBackButton />
 
-        <ScrollContainer scrollKey='reblogs'>
+        <ScrollContainer scrollKey='reblogs' shouldUpdateScroll={this.shouldUpdateScroll}>
           <div className='scrollable reblogs'>
             {accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} />)}
           </div>
diff --git a/app/javascript/flavours/glitch/features/standalone/community_timeline/index.js b/app/javascript/flavours/glitch/features/standalone/community_timeline/index.js
index d4ef3ce48..2b67e836a 100644
--- a/app/javascript/flavours/glitch/features/standalone/community_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/standalone/community_timeline/index.js
@@ -47,10 +47,6 @@ export default class CommunityTimeline extends React.PureComponent {
     this.props.dispatch(expandCommunityTimeline({ maxId }));
   }
 
-  shouldUpdateScroll = (prevRouterProps, { location }) => {
-    return !(location.state && location.state.mastodonModalOpen)
-  }
-
   render () {
     const { intl } = this.props;
 
@@ -66,7 +62,6 @@ export default class CommunityTimeline extends React.PureComponent {
           timelineId='community'
           onLoadMore={this.handleLoadMore}
           scrollKey='standalone_public_timeline'
-          shouldUpdateScroll={this.shouldUpdateScroll}
           trackScroll={false}
         />
       </Column>
diff --git a/app/javascript/flavours/glitch/features/standalone/hashtag_timeline/index.js b/app/javascript/flavours/glitch/features/standalone/hashtag_timeline/index.js
index d2b1971ec..dc02f1c91 100644
--- a/app/javascript/flavours/glitch/features/standalone/hashtag_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/standalone/hashtag_timeline/index.js
@@ -41,10 +41,6 @@ export default class HashtagTimeline extends React.PureComponent {
     this.props.dispatch(expandHashtagTimeline(this.props.hashtag, { maxId }));
   }
 
-  shouldUpdateScroll = (prevRouterProps, { location }) => {
-    return !(location.state && location.state.mastodonModalOpen)
-  }
-
   render () {
     const { hashtag } = this.props;
 
@@ -59,7 +55,6 @@ export default class HashtagTimeline extends React.PureComponent {
         <StatusListContainer
           trackScroll={false}
           scrollKey='standalone_hashtag_timeline'
-          shouldUpdateScroll={this.shouldUpdateScroll}
           timelineId={`hashtag:${hashtag}`}
           onLoadMore={this.handleLoadMore}
         />
diff --git a/app/javascript/flavours/glitch/features/status/components/action_bar.js b/app/javascript/flavours/glitch/features/status/components/action_bar.js
index 413833a79..009aa49eb 100644
--- a/app/javascript/flavours/glitch/features/status/components/action_bar.js
+++ b/app/javascript/flavours/glitch/features/status/components/action_bar.js
@@ -4,7 +4,7 @@ import IconButton from 'flavours/glitch/components/icon_button';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
 import { defineMessages, injectIntl } from 'react-intl';
-import { me } from 'flavours/glitch/util/initial_state';
+import { me, isStaff } from 'flavours/glitch/util/initial_state';
 
 const messages = defineMessages({
   delete: { id: 'status.delete', defaultMessage: 'Delete' },
@@ -26,6 +26,8 @@ const messages = defineMessages({
   pin: { id: 'status.pin', defaultMessage: 'Pin on profile' },
   unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
   embed: { id: 'status.embed', defaultMessage: 'Embed' },
+  admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
+  admin_status: { id: 'status.admin_status', defaultMessage: 'Open this status in the moderation interface' },
 });
 
 @injectIntl
@@ -146,6 +148,11 @@ export default class ActionBar extends React.PureComponent {
       menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick });
       menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
       menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
+      if (isStaff) {
+        menu.push(null);
+        menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
+        menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` });
+      }
     }
 
     const shareButton = ('share' in navigator) && status.get('visibility') === 'public' && (
diff --git a/app/javascript/flavours/glitch/features/status/components/card.js b/app/javascript/flavours/glitch/features/status/components/card.js
index 680bf63ab..b52f3c4fa 100644
--- a/app/javascript/flavours/glitch/features/status/components/card.js
+++ b/app/javascript/flavours/glitch/features/status/components/card.js
@@ -20,6 +20,39 @@ const getHostname = url => {
   return parser.hostname;
 };
 
+const trim = (text, len) => {
+  const cut = text.indexOf(' ', len);
+
+  if (cut === -1) {
+    return text;
+  }
+
+  return text.substring(0, cut) + (text.length > len ? '…' : '');
+};
+
+const domParser = new DOMParser();
+
+const addAutoPlay = html => {
+  const document = domParser.parseFromString(html, 'text/html').documentElement;
+  const iframe = document.querySelector('iframe');
+
+  if (iframe) {
+    if (iframe.src.indexOf('?') !== -1) {
+      iframe.src += '&';
+    } else {
+      iframe.src += '?';
+    }
+
+    iframe.src += 'autoplay=1&auto_play=1';
+
+    // DOM parser creates html/body elements around original HTML fragment,
+    // so we need to get innerHTML out of the body and not the entire document
+    return document.querySelector('body').innerHTML;
+  }
+
+  return html;
+};
+
 export default class Card extends React.PureComponent {
 
   static propTypes = {
@@ -33,9 +66,16 @@ export default class Card extends React.PureComponent {
   };
 
   state = {
-    width: 0,
+    width: 280,
+    embedded: false,
   };
 
+  componentWillReceiveProps (nextProps) {
+    if (this.props.card !== nextProps.card) {
+      this.setState({ embedded: false });
+    }
+  }
+
   handlePhotoClick = () => {
     const { card, onOpenMedia } = this.props;
 
@@ -43,7 +83,7 @@ export default class Card extends React.PureComponent {
       Immutable.fromJS([
         {
           type: 'image',
-          url: card.get('url'),
+          url: card.get('embed_url'),
           description: card.get('title'),
           meta: {
             original: {
@@ -57,56 +97,14 @@ export default class Card extends React.PureComponent {
     );
   };
 
-  renderLink () {
-    const { card, maxDescription } = this.props;
-
-    let image    = '';
-    let provider = card.get('provider_name');
-
-    if (card.get('image')) {
-      image = (
-        <div className='status-card__image'>
-          <img src={card.get('image')} alt={card.get('title')} className='status-card__image-image' width={card.get('width')} height={card.get('height')} />
-        </div>
-      );
-    }
-
-    if (provider.length < 1) {
-      provider = decodeIDNA(getHostname(card.get('url')));
-    }
-
-    const className = classnames('status-card', {
-      'horizontal': card.get('width') > card.get('height'),
-    });
-
-    return (
-      <a href={card.get('url')} className={className} target='_blank' rel='noopener'>
-        {image}
-
-        <div className='status-card__content'>
-          <strong className='status-card__title' title={card.get('title')}>{card.get('title')}</strong>
-          <p className='status-card__description'>{(card.get('description') || '').substring(0, maxDescription)}</p>
-          <span className='status-card__host'>{provider}</span>
-        </div>
-      </a>
-    );
-  }
-
-  renderPhoto () {
+  handleEmbedClick = () => {
     const { card } = this.props;
 
-    return (
-      <img
-        className='status-card-photo'
-        onClick={this.handlePhotoClick}
-        role='button'
-        tabIndex='0'
-        src={card.get('url')}
-        alt={card.get('title')}
-        width={card.get('width')}
-        height={card.get('height')}
-      />
-    );
+    if (card.get('type') === 'photo') {
+      this.handlePhotoClick();
+    } else {
+      this.setState({ embedded: true });
+    }
   }
 
   setRef = c => {
@@ -117,7 +115,7 @@ export default class Card extends React.PureComponent {
 
   renderVideo () {
     const { card }  = this.props;
-    const content   = { __html: card.get('html') };
+    const content   = { __html: addAutoPlay(card.get('html')) };
     const { width } = this.state;
     const ratio     = card.get('width') / card.get('height');
     const height    = card.get('width') > card.get('height') ? (width / ratio) : (width * ratio);
@@ -125,7 +123,7 @@ export default class Card extends React.PureComponent {
     return (
       <div
         ref={this.setRef}
-        className='status-card-video'
+        className='status-card__image status-card-video'
         dangerouslySetInnerHTML={content}
         style={{ height }}
       />
@@ -133,23 +131,76 @@ export default class Card extends React.PureComponent {
   }
 
   render () {
-    const { card } = this.props;
+    const { card, maxDescription } = this.props;
+    const { width, embedded }      = this.state;
 
     if (card === null) {
       return null;
     }
 
-    switch(card.get('type')) {
-    case 'link':
-      return this.renderLink();
-    case 'photo':
-      return this.renderPhoto();
-    case 'video':
-      return this.renderVideo();
-    case 'rich':
-    default:
-      return null;
+    const provider    = card.get('provider_name').length === 0 ? decodeIDNA(getHostname(card.get('url'))) : card.get('provider_name');
+    const horizontal  = card.get('width') > card.get('height') && (card.get('width') + 100 >= width) || card.get('type') !== 'link';
+    const className   = classnames('status-card', { horizontal });
+    const interactive = card.get('type') !== 'link';
+    const title       = interactive ? <a className='status-card__title' href={card.get('url')} title={card.get('title')} rel='noopener' target='_blank'><strong>{card.get('title')}</strong></a> : <strong className='status-card__title' title={card.get('title')}>{card.get('title')}</strong>;
+    const ratio       = card.get('width') / card.get('height');
+    const height      = card.get('width') > card.get('height') ? (width / ratio) : (width * ratio);
+
+    const description = (
+      <div className='status-card__content'>
+        {title}
+        {!horizontal && <p className='status-card__description'>{trim(card.get('description') || '', maxDescription)}</p>}
+        <span className='status-card__host'>{provider}</span>
+      </div>
+    );
+
+    let embed     = '';
+    let thumbnail = <div style={{ backgroundImage: `url(${card.get('image')})`, width: horizontal ? width : null, height: horizontal ? height : null }} className='status-card__image-image' />;
+
+    if (interactive) {
+      if (embedded) {
+        embed = this.renderVideo();
+      } else {
+        let iconVariant = 'play';
+
+        if (card.get('type') === 'photo') {
+          iconVariant = 'search-plus';
+        }
+
+        embed = (
+          <div className='status-card__image'>
+            {thumbnail}
+
+            <div className='status-card__actions'>
+              <div>
+                <button onClick={this.handleEmbedClick}><i className={`fa fa-${iconVariant}`} /></button>
+                <a href={card.get('url')} target='_blank' rel='noopener'><i className='fa fa-external-link' /></a>
+              </div>
+            </div>
+          </div>
+        );
+      }
+
+      return (
+        <div className={className} ref={this.setRef}>
+          {embed}
+          {description}
+        </div>
+      );
+    } else if (card.get('image')) {
+      embed = (
+        <div className='status-card__image'>
+          {thumbnail}
+        </div>
+      );
     }
+
+    return (
+      <a href={card.get('url')} className={className} target='_blank' rel='noopener' ref={this.setRef}>
+        {embed}
+        {description}
+      </a>
+    );
   }
 
 }
diff --git a/app/javascript/flavours/glitch/features/status/components/detailed_status.js b/app/javascript/flavours/glitch/features/status/components/detailed_status.js
index ee9eb02c6..2e61e6d8c 100644
--- a/app/javascript/flavours/glitch/features/status/components/detailed_status.js
+++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.js
@@ -77,6 +77,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
             sensitive={status.get('sensitive')}
             media={status.get('media_attachments')}
             letterbox={settings.getIn(['media', 'letterbox'])}
+            fullwidth={settings.getIn(['media', 'fullwidth'])}
             onOpenMedia={this.props.onOpenMedia}
           />
         );
diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js
index 0697b49ad..5759a575c 100644
--- a/app/javascript/flavours/glitch/features/status/index.js
+++ b/app/javascript/flavours/glitch/features/status/index.js
@@ -105,7 +105,7 @@ export default class Status extends ImmutablePureComponent {
     if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
       this._scrolledIntoView = false;
       this.props.dispatch(fetchStatus(nextProps.params.statusId));
-      this.setState({ isExpanded: autoUnfoldCW(nextProps.settings, nextProps.status) });
+      this.setState({ isExpanded: autoUnfoldCW(nextProps.settings, nextProps.status), threadExpanded: undefined });
     }
   }
 
diff --git a/app/javascript/flavours/glitch/features/ui/components/columns_area.js b/app/javascript/flavours/glitch/features/ui/components/columns_area.js
index 8fde279c7..71cb7e8c9 100644
--- a/app/javascript/flavours/glitch/features/ui/components/columns_area.js
+++ b/app/javascript/flavours/glitch/features/ui/components/columns_area.js
@@ -1,6 +1,6 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import { injectIntl } from 'react-intl';
+import { defineMessages, injectIntl } from 'react-intl';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 
@@ -30,6 +30,12 @@ const componentMap = {
   'LIST': ListTimeline,
 };
 
+const shouldHideFAB = path => path.match(/^\/statuses\//);
+
+const messages = defineMessages({
+  publish: { id: 'compose_form.publish', defaultMessage: 'Toot' },
+});
+
 @component => injectIntl(component, { withRef: true })
 export default class ColumnsArea extends ImmutablePureComponent {
 
@@ -147,14 +153,14 @@ export default class ColumnsArea extends ImmutablePureComponent {
   }
 
   render () {
-    const { columns, children, singleColumn } = this.props;
+    const { columns, children, singleColumn, intl } = this.props;
     const { shouldAnimate } = this.state;
 
     const columnIndex = getIndex(this.context.router.history.location.pathname);
     this.pendingIndex = null;
 
     if (singleColumn) {
-      const floatingActionButton = this.context.router.history.location.pathname === '/statuses/new' ? null : <Link key='floating-action-button' to='/statuses/new' className='floating-action-button'><i className='fa fa-pencil' /></Link>;
+      const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : <Link key='floating-action-button' to='/statuses/new' className='floating-action-button' aria-label={intl.formatMessage(messages.publish)}><i className='fa fa-pencil' /></Link>;
 
       return columnIndex !== -1 ? [
         <ReactSwipeableViews key='content' index={columnIndex} onChangeIndex={this.handleSwipe} onTransitionEnd={this.handleAnimationEnd} animateTransitions={shouldAnimate} springConfig={{ duration: '400ms', delay: '0s', easeFunction: 'ease' }} style={{ height: '100%' }}>
diff --git a/app/javascript/flavours/glitch/features/ui/components/media_modal.js b/app/javascript/flavours/glitch/features/ui/components/media_modal.js
index d4fd45d4d..1f3ac18ea 100644
--- a/app/javascript/flavours/glitch/features/ui/components/media_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/media_modal.js
@@ -129,7 +129,7 @@ export default class MediaModal extends ImmutablePureComponent {
             startTime={time || 0}
             onCloseVideo={onClose}
             detailed
-            description={image.get('description')}
+            alt={image.get('description')}
             key={image.get('url')}
           />
         );
diff --git a/app/javascript/flavours/glitch/features/ui/components/report_modal.js b/app/javascript/flavours/glitch/features/ui/components/report_modal.js
index 81643b6c2..a139394ac 100644
--- a/app/javascript/flavours/glitch/features/ui/components/report_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/report_modal.js
@@ -106,6 +106,7 @@ export default class ReportModal extends ImmutablePureComponent {
               onChange={this.handleCommentChange}
               onKeyDown={this.handleKeyDown}
               disabled={isSubmitting}
+              autoFocus
             />
 
             {domain && (
diff --git a/app/javascript/flavours/glitch/features/ui/components/tabs_bar.js b/app/javascript/flavours/glitch/features/ui/components/tabs_bar.js
index b2fee21e1..b44a21a42 100644
--- a/app/javascript/flavours/glitch/features/ui/components/tabs_bar.js
+++ b/app/javascript/flavours/glitch/features/ui/components/tabs_bar.js
@@ -4,10 +4,34 @@ import { NavLink, withRouter } from 'react-router-dom';
 import { FormattedMessage, injectIntl } from 'react-intl';
 import { debounce } from 'lodash';
 import { isUserTouching } from 'flavours/glitch/util/is_mobile';
+import { connect } from 'react-redux';
+
+const mapStateToProps = state => ({
+  unreadNotifications: state.getIn(['notifications', 'unread']),
+  showBadge: state.getIn(['local_settings', 'notifications', 'tab_badge']),
+});
+
+@connect(mapStateToProps)
+class NotificationsIcon extends React.PureComponent {
+  static propTypes = {
+    unreadNotifications: PropTypes.number,
+    showBadge: PropTypes.bool,
+  };
+
+  render() {
+    const { unreadNotifications, showBadge } = this.props;
+    return (
+      <span className='icon-badge-wrapper'>
+        <i className='fa fa-fw fa-bell' />
+        { showBadge && unreadNotifications > 0 && <div className='icon-badge' />}
+      </span>
+    );
+  }
+}
 
 export const links = [
   <NavLink className='tabs-bar__link primary' to='/timelines/home' data-preview-title-id='column.home' data-preview-icon='home' ><i className='fa fa-fw fa-home' /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></NavLink>,
-  <NavLink className='tabs-bar__link primary' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><i className='fa fa-fw fa-bell' /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink>,
+  <NavLink className='tabs-bar__link primary' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><NotificationsIcon /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink>,
 
   <NavLink className='tabs-bar__link secondary' to='/timelines/public/local' data-preview-title-id='column.community' data-preview-icon='users' ><i className='fa fa-fw fa-users' /><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></NavLink>,
   <NavLink className='tabs-bar__link secondary' exact to='/timelines/public' data-preview-title-id='column.public' data-preview-icon='globe' ><i className='fa fa-fw fa-globe' /><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></NavLink>,
diff --git a/app/javascript/flavours/glitch/features/ui/components/video_modal.js b/app/javascript/flavours/glitch/features/ui/components/video_modal.js
index e0cb7fc09..69e0ee46e 100644
--- a/app/javascript/flavours/glitch/features/ui/components/video_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/video_modal.js
@@ -24,7 +24,7 @@ export default class VideoModal extends ImmutablePureComponent {
             startTime={time}
             onCloseVideo={onClose}
             detailed
-            description={media.get('description')}
+            alt={media.get('description')}
           />
         </div>
       </div>
diff --git a/app/javascript/flavours/glitch/features/ui/components/zoomable_image.js b/app/javascript/flavours/glitch/features/ui/components/zoomable_image.js
index 0a0a4d41a..3f6562bc9 100644
--- a/app/javascript/flavours/glitch/features/ui/components/zoomable_image.js
+++ b/app/javascript/flavours/glitch/features/ui/components/zoomable_image.js
@@ -137,6 +137,7 @@ export default class ZoomableImage extends React.PureComponent {
           role='presentation'
           ref={this.setImageRef}
           alt={alt}
+          title={alt}
           src={src}
           style={{
             transform: `scale(${scale})`,
diff --git a/app/javascript/flavours/glitch/features/ui/index.js b/app/javascript/flavours/glitch/features/ui/index.js
index 1cff94321..ecbac1f8f 100644
--- a/app/javascript/flavours/glitch/features/ui/index.js
+++ b/app/javascript/flavours/glitch/features/ui/index.js
@@ -10,13 +10,14 @@ import { isMobile } from 'flavours/glitch/util/is_mobile';
 import { debounce } from 'lodash';
 import { uploadCompose, resetCompose } from 'flavours/glitch/actions/compose';
 import { expandHomeTimeline } from 'flavours/glitch/actions/timelines';
-import { expandNotifications } from 'flavours/glitch/actions/notifications';
+import { expandNotifications, notificationsSetVisibility } from 'flavours/glitch/actions/notifications';
 import { fetchFilters } from 'flavours/glitch/actions/filters';
 import { clearHeight } from 'flavours/glitch/actions/height_cache';
 import { WrappedSwitch, WrappedRoute } from 'flavours/glitch/util/react_router_helpers';
 import UploadArea from './components/upload_area';
 import ColumnsAreaContainer from './containers/columns_area_container';
 import classNames from 'classnames';
+import Favico from 'favico.js';
 import {
   Drawer,
   Status,
@@ -59,11 +60,14 @@ const messages = defineMessages({
 });
 
 const mapStateToProps = state => ({
-  hasComposingText: state.getIn(['compose', 'text']) !== '',
+  hasComposingText: state.getIn(['compose', 'text']).trim().length !== 0,
+  hasMediaAttachments: state.getIn(['compose', 'media_attachments']).size > 0,
   layout: state.getIn(['local_settings', 'layout']),
   isWide: state.getIn(['local_settings', 'stretch']),
   navbarUnder: state.getIn(['local_settings', 'navbar_under']),
   dropdownMenuIsOpen: state.getIn(['dropdown_menu', 'openId']) !== null,
+  unreadNotifications: state.getIn(['notifications', 'unread']),
+  showFaviconBadge: state.getIn(['local_settings', 'notifications', 'favicon_badge']),
 });
 
 const keyMap = {
@@ -110,11 +114,14 @@ export default class UI extends React.Component {
     navbarUnder: PropTypes.bool,
     isComposing: PropTypes.bool,
     hasComposingText: PropTypes.bool,
+    hasMediaAttachments: PropTypes.bool,
     match: PropTypes.object.isRequired,
     location: PropTypes.object.isRequired,
     history: PropTypes.object.isRequired,
     intl: PropTypes.object.isRequired,
     dropdownMenuIsOpen: PropTypes.bool,
+    unreadNotifications: PropTypes.number,
+    showFaviconBadge: PropTypes.bool,
   };
 
   state = {
@@ -123,9 +130,9 @@ export default class UI extends React.Component {
   };
 
   handleBeforeUnload = (e) => {
-    const { intl, hasComposingText } = this.props;
+    const { intl, hasComposingText, hasMediaAttachments } = this.props;
 
-    if (hasComposingText) {
+    if (hasComposingText || hasMediaAttachments) {
       // Setting returnValue to any string causes confirmation dialog.
       // Many browsers no longer display this text to users,
       // but we set user-friendly message for other browsers, e.g. Edge.
@@ -206,7 +213,27 @@ export default class UI extends React.Component {
     }
   }
 
+  handleVisibilityChange = () => {
+    const visibility = !document[this.visibilityHiddenProp];
+    this.props.dispatch(notificationsSetVisibility(visibility));
+  }
+
   componentWillMount () {
+    if (typeof document.hidden !== 'undefined') { // Opera 12.10 and Firefox 18 and later support
+      this.visibilityHiddenProp = 'hidden';
+      this.visibilityChange = 'visibilitychange';
+    } else if (typeof document.msHidden !== 'undefined') {
+      this.visibilityHiddenProp = 'msHidden';
+      this.visibilityChange = 'msvisibilitychange';
+    } else if (typeof document.webkitHidden !== 'undefined') {
+      this.visibilityHiddenProp = 'webkitHidden';
+      this.visibilityChange = 'webkitvisibilitychange';
+    }
+    if (this.visibilityChange !== undefined) {
+      document.addEventListener(this.visibilityChange, this.handleVisibilityChange, false);
+      this.handleVisibilityChange();
+    }
+
     window.addEventListener('beforeunload', this.handleBeforeUnload, false);
     window.addEventListener('resize', this.handleResize, { passive: true });
     document.addEventListener('dragenter', this.handleDragEnter, false);
@@ -219,6 +246,8 @@ export default class UI extends React.Component {
       navigator.serviceWorker.addEventListener('message', this.handleServiceWorkerPostMessage);
     }
 
+    this.favicon = new Favico({ animation:"none" });
+
     this.props.dispatch(expandHomeTimeline());
     this.props.dispatch(expandNotifications());
     setTimeout(() => this.props.dispatch(fetchFilters()), 500);
@@ -247,9 +276,19 @@ export default class UI extends React.Component {
     if (![this.props.location.pathname, '/'].includes(prevProps.location.pathname)) {
       this.columnsAreaNode.handleChildrenContentChange();
     }
+    if (this.props.unreadNotifications != prevProps.unreadNotifications ||
+        this.props.showFaviconBadge != prevProps.showFaviconBadge) {
+      if (this.favicon) {
+        this.favicon.badge(this.props.showFaviconBadge ? this.props.unreadNotifications : 0);
+      }
+    }
   }
 
   componentWillUnmount () {
+    if (this.visibilityChange !== undefined) {
+      document.removeEventListener(this.visibilityChange, this.handleVisibilityChange);
+    }
+
     window.removeEventListener('beforeunload', this.handleBeforeUnload);
     window.removeEventListener('resize', this.handleResize);
     document.removeEventListener('dragenter', this.handleDragEnter);
diff --git a/app/javascript/flavours/glitch/features/video/index.js b/app/javascript/flavours/glitch/features/video/index.js
index 7e284a0bc..5cbe01f26 100644
--- a/app/javascript/flavours/glitch/features/video/index.js
+++ b/app/javascript/flavours/glitch/features/video/index.js
@@ -101,6 +101,7 @@ export default class Video extends React.PureComponent {
     fullwidth: PropTypes.bool,
     detailed: PropTypes.bool,
     inline: PropTypes.bool,
+    preventPlayback: PropTypes.bool,
     intl: PropTypes.object.isRequired,
   };
 
@@ -134,7 +135,10 @@ export default class Video extends React.PureComponent {
     this.seek = c;
   }
 
-  handleClickRoot = e => e.stopPropagation();
+  handleMouseDownRoot = e => {
+    e.preventDefault();
+    e.stopPropagation();
+  }
 
   handlePlay = () => {
     this.setState({ paused: false });
@@ -215,6 +219,12 @@ export default class Video extends React.PureComponent {
     document.removeEventListener('MSFullscreenChange', this.handleFullscreenChange, true);
   }
 
+  componentDidUpdate (prevProps) {
+    if (this.video && this.state.revealed && this.props.preventPlayback && !prevProps.preventPlayback) {
+      this.video.pause();
+    }
+  }
+
   handleFullscreenChange = () => {
     this.setState({ fullscreen: isFullscreen() });
   }
@@ -254,11 +264,12 @@ export default class Video extends React.PureComponent {
   }
 
   handleOpenVideo = () => {
-    const { src, preview, width, height } = this.props;
+    const { src, preview, width, height, alt } = this.props;
     const media = fromJS({
       type: 'video',
       url: src,
       preview_url: preview,
+      description: alt,
       width,
       height,
     });
@@ -311,7 +322,7 @@ export default class Video extends React.PureComponent {
         ref={this.setPlayerRef}
         onMouseEnter={this.handleMouseEnter}
         onMouseLeave={this.handleMouseLeave}
-        onClick={this.handleClickRoot}
+        onMouseDown={this.handleMouseDownRoot}
         tabIndex={0}
       >
         <video
diff --git a/app/javascript/flavours/glitch/locales/ja.js b/app/javascript/flavours/glitch/locales/ja.js
index f558d7ab7..c323956c6 100644
--- a/app/javascript/flavours/glitch/locales/ja.js
+++ b/app/javascript/flavours/glitch/locales/ja.js
@@ -4,13 +4,14 @@ const messages = {
   'getting_started.open_source_notice': 'Glitchsocは{Mastodon}によるフリーなオープンソースソフトウェアです。誰でもGitHub({github})から開発に參加したり、問題を報告したりできます。',
   'layout.auto': '自動',
   'layout.current_is': 'あなたの現在のレイアウト:',
-  'layout.desktop': 'Desktop',
-  'layout.mobile': 'Mobile',
+  'layout.desktop': 'デスクトップ',
+  'layout.single': 'モバイル',
   'navigation_bar.app_settings': 'アプリ設定',
   'getting_started.onboarding': '解説を表示',
   'onboarding.page_one.federation': '{domain}はMastodonのインスタンスです。Mastodonとは、独立したサーバが連携して作るソーシャルネットワークです。これらのサーバーをインスタンスと呼びます。',
   'onboarding.page_one.welcome': '{domain}へようこそ!',
   'onboarding.page_six.github': '{domain}はGlitchsocを使用しています。Glitchsocは{Mastodon}のフレンドリーな{fork}で、どんなMastodonアプリやインスタンスとも互換性があります。Glitchsocは完全に無料で、オープンソースです。{github}でバグ報告や機能要望あるいは貢獻をすることが可能です。',
+  'settings.always_show_spoilers_field': '常にコンテンツワーニング設定を表示する(指定がない場合は通常投稿)',
   'settings.auto_collapse': '自動折りたたみ',
   'settings.auto_collapse_all': 'すべて',
   'settings.auto_collapse_lengthy': '長いトゥート',
@@ -20,7 +21,12 @@ const messages = {
   'settings.auto_collapse_replies': '返信',
   'settings.close': '閉じる',
   'settings.collapsed_statuses': 'トゥート',
+  'settings.confirm_missing_media_description': '画像に対する補助記載がないときに投稿前の警告を表示する',
+  'settings.content_warnings': 'コンテンツワーニング',
+  'settings.content_warnings_filter': '説明に指定した文字が含まれているものを自動で展開しないようにする',
+  'settings.content_warnings.regexp': '正規表現',
   'settings.enable_collapsed': 'トゥート折りたたみを有効にする',
+  'settings.enable_content_warnings_auto_unfold': 'コンテンツワーニング指定されている投稿を常に表示する',
   'settings.general': '一般',
   'settings.image_backgrounds': '画像背景',
   'settings.image_backgrounds_media': '折りたまれたメディア付きトゥートをプレビュー',
@@ -28,15 +34,28 @@ const messages = {
   'settings.media': 'メディア',
   'settings.media_letterbox': 'メディアをレターボックス式で表示',
   'settings.media_fullwidth': '全幅メディアプレビュー',
+  'settings.navbar_under': 'ナビを画面下部に移動させる(モバイル レイアウトのみ)',
+  'settings.notifications.favicon_badge': '通知アイコンに未読件数を表示する',
+  'settings.notifications_opts': '通知の設定',
+  'settings.notifications.tab_badge': '未読の通知があるとき、通知アイコンにマークを表示する',
   'settings.preferences': 'ユーザー設定',
-  'settings.wide_view': 'ワイドビュー(Desktopレイアウトのみ)',
-  'settings.navbar_under': 'ナビを画面下部に移動させる(Mobileレイアウトのみ)',
+  'settings.wide_view': 'ワイドビュー(デスクトップ レイアウトのみ)',
   'settings.compose_box_opts': 'コンポーズボックス設定',
+  'settings.show_reply_counter': '投稿に対するリプライの数を表示する',
   'settings.side_arm': 'セカンダリートゥートボタン',
+  'settings.side_arm.none': '表示しない',
+  'settings.side_arm_reply_mode': '返信時の投稿範囲',
+  'settings.side_arm_reply_mode.copy': '返信先の投稿範囲を利用する',
+  'settings.side_arm_reply_mode.keep': 'セカンダリートゥートボタンの設定を維持する',
+  'settings.side_arm_reply_mode.restrict': '返信先の投稿範囲に制限する',
   'settings.layout': 'レイアウト',
+  'settings.layout_opts': 'レイアウトの設定',
   'status.collapse': '折りたたむ',
   'status.uncollapse': '折りたたみを解除',
 
+  'confirmations.missing_media_description.message': '少なくとも1つの画像に視聴覚障害者のための画像説明が付与されていません。すべての画像に対して説明を付与することを望みます。',
+  'confirmations.missing_media_description.confirm': 'このまま投稿',
+
   'favourite_modal.combo': '次からは {combo} を押せば、これをスキップできます。',
 
   'home.column_settings.show_direct': 'DMを表示',
@@ -68,4 +87,4 @@ const messages = {
   'column.bookmarks': 'ブックマーク'
 };
 
-export default Object.assign({}, inherited, messages);
+export default Object.assign({}, inherited, messages);
\ No newline at end of file
diff --git a/app/javascript/flavours/glitch/middleware/sounds.js b/app/javascript/flavours/glitch/middleware/sounds.js
index 3d1e3eaba..9f1bc02b9 100644
--- a/app/javascript/flavours/glitch/middleware/sounds.js
+++ b/app/javascript/flavours/glitch/middleware/sounds.js
@@ -15,7 +15,7 @@ const play = audio => {
     if (typeof audio.fastSeek === 'function') {
       audio.fastSeek(0);
     } else {
-      audio.seek(0);
+      audio.currentTime = 0;
     }
   }
 
diff --git a/app/javascript/flavours/glitch/reducers/compose.js b/app/javascript/flavours/glitch/reducers/compose.js
index 594d70ee2..0ddff707e 100644
--- a/app/javascript/flavours/glitch/reducers/compose.js
+++ b/app/javascript/flavours/glitch/reducers/compose.js
@@ -314,8 +314,12 @@ export default function compose(state = initialState, action) {
       map.set('idempotencyKey', uuid());
 
       if (action.status.get('spoiler_text').length > 0) {
+        let spoiler_text = action.status.get('spoiler_text');
+        if (!spoiler_text.match(/^re[: ]/i)) {
+          spoiler_text = 're: '.concat(spoiler_text);
+        }
         map.set('spoiler', true);
-        map.set('spoiler_text', action.status.get('spoiler_text'));
+        map.set('spoiler_text', spoiler_text);
       } else {
         map.set('spoiler', false);
         map.set('spoiler_text', '');
diff --git a/app/javascript/flavours/glitch/reducers/dropdown_menu.js b/app/javascript/flavours/glitch/reducers/dropdown_menu.js
index 5449884cc..36fd4f132 100644
--- a/app/javascript/flavours/glitch/reducers/dropdown_menu.js
+++ b/app/javascript/flavours/glitch/reducers/dropdown_menu.js
@@ -4,12 +4,12 @@ import {
   DROPDOWN_MENU_CLOSE,
 } from '../actions/dropdown_menu';
 
-const initialState = Immutable.Map({ openId: null, placement: null });
+const initialState = Immutable.Map({ openId: null, placement: null, keyboard: false });
 
 export default function dropdownMenu(state = initialState, action) {
   switch (action.type) {
   case DROPDOWN_MENU_OPEN:
-    return state.merge({ openId: action.id, placement: action.placement });
+    return state.merge({ openId: action.id, placement: action.placement, keyboard: action.keyboard });
   case DROPDOWN_MENU_CLOSE:
     return state.get('openId') === action.id ? state.set('openId', null) : state;
   default:
diff --git a/app/javascript/flavours/glitch/reducers/local_settings.js b/app/javascript/flavours/glitch/reducers/local_settings.js
index 063ae3943..260a9f08f 100644
--- a/app/javascript/flavours/glitch/reducers/local_settings.js
+++ b/app/javascript/flavours/glitch/reducers/local_settings.js
@@ -14,6 +14,7 @@ const initialState = ImmutableMap({
   show_reply_count : false,
   always_show_spoilers_field: false,
   confirm_missing_media_description: false,
+  preselect_on_reply: true,
   content_warnings : ImmutableMap({
     auto_unfold : false,
     filter      : null,
@@ -37,6 +38,10 @@ const initialState = ImmutableMap({
     letterbox   : true,
     fullwidth   : true,
   }),
+  notifications : ImmutableMap({
+    favicon_badge : false,
+    tab_badge     : true,
+  }),
 });
 
 const hydrate = (state, localSettings) => state.mergeDeep(localSettings);
diff --git a/app/javascript/flavours/glitch/reducers/notifications.js b/app/javascript/flavours/glitch/reducers/notifications.js
index dc820b476..0b816e85e 100644
--- a/app/javascript/flavours/glitch/reducers/notifications.js
+++ b/app/javascript/flavours/glitch/reducers/notifications.js
@@ -1,4 +1,7 @@
 import {
+  NOTIFICATIONS_MOUNT,
+  NOTIFICATIONS_UNMOUNT,
+  NOTIFICATIONS_SET_VISIBILITY,
   NOTIFICATIONS_UPDATE,
   NOTIFICATIONS_EXPAND_SUCCESS,
   NOTIFICATIONS_EXPAND_REQUEST,
@@ -24,9 +27,12 @@ const initialState = ImmutableMap({
   items: ImmutableList(),
   hasMore: true,
   top: true,
+  mounted: 0,
   unread: 0,
+  lastReadId: '0',
   isLoading: false,
   cleaningMode: false,
+  isTabVisible: true,
   // notification removal mark of new notifs loaded whilst cleaningMode is true.
   markNewForDelete: false,
 });
@@ -40,9 +46,11 @@ const notificationToMap = (state, notification) => ImmutableMap({
 });
 
 const normalizeNotification = (state, notification) => {
-  const top = state.get('top');
+  const top = !shouldCountUnreadNotifications(state);
 
-  if (!top) {
+  if (top) {
+    state = state.set('lastReadId', notification.id);
+  } else {
     state = state.update('unread', unread => unread + 1);
   }
 
@@ -56,6 +64,8 @@ const normalizeNotification = (state, notification) => {
 };
 
 const expandNormalizedNotifications = (state, notifications, next) => {
+  const top = !(shouldCountUnreadNotifications(state));
+  const lastReadId = state.get('lastReadId');
   let items = ImmutableList();
 
   notifications.forEach((n, i) => {
@@ -77,6 +87,14 @@ const expandNormalizedNotifications = (state, notifications, next) => {
       });
     }
 
+    if (top) {
+      if (!items.isEmpty()) {
+        mutable.update('lastReadId', id => compareId(id, items.first().get('id')) > 0 ? id : items.first().get('id'));
+      }
+    } else {
+      mutable.update('unread', unread => unread + items.filter(item => compareId(item.get('id'), lastReadId) > 0).size);
+    }
+
     if (!next) {
       mutable.set('hasMore', true);
     }
@@ -89,15 +107,29 @@ const filterNotifications = (state, relationship) => {
   return state.update('items', list => list.filterNot(item => item !== null && item.get('account') === relationship.id));
 };
 
+const clearUnread = (state) => {
+  state = state.set('unread', 0);
+  const lastNotification = state.get('items').find(item => item !== null);
+  return state.set('lastReadId', lastNotification ? lastNotification.get('id') : '0');
+}
+
 const updateTop = (state, top) => {
-  if (top) {
-    state = state.set('unread', 0);
+  state = state.set('top', top);
+
+  if (!shouldCountUnreadNotifications(state)) {
+    state = clearUnread(state);
   }
 
   return state.set('top', top);
 };
 
 const deleteByStatus = (state, statusId) => {
+  const top = !(shouldCountUnreadNotifications(state));
+  if (!top) {
+    const lastReadId = state.get('lastReadId');
+    const deletedUnread = state.get('items').filter(item => item !== null && item.get('status') === statusId && compareId(item.get('id'), lastReadId) > 0);
+    state = state.update('unread', unread => unread - deletedUnread.size);
+  }
   return state.update('items', list => list.filterNot(item => item !== null && item.get('status') === statusId));
 };
 
@@ -129,10 +161,36 @@ const deleteMarkedNotifs = (state) => {
   return state.update('items', list => list.filterNot(item => item.get('markedForDelete')));
 };
 
+const updateMounted = (state) => {
+  state = state.update('mounted', count => count + 1);
+  if (!shouldCountUnreadNotifications(state)) {
+    state = clearUnread(state);
+  }
+  return state;
+};
+
+const updateVisibility = (state, visibility) => {
+  state = state.set('isTabVisible', visibility);
+  if (!shouldCountUnreadNotifications(state)) {
+    state = clearUnread(state);
+  }
+  return state;
+};
+
+const shouldCountUnreadNotifications = (state) => {
+  return !(state.get('isTabVisible') && state.get('top') && state.get('mounted') > 0);
+};
+
 export default function notifications(state = initialState, action) {
   let st;
 
   switch(action.type) {
+  case NOTIFICATIONS_MOUNT:
+    return updateMounted(state);
+  case NOTIFICATIONS_UNMOUNT:
+    return state.update('mounted', count => count - 1);
+  case NOTIFICATIONS_SET_VISIBILITY:
+    return updateVisibility(state, action.visibility);
   case NOTIFICATIONS_EXPAND_REQUEST:
   case NOTIFICATIONS_DELETE_MARKED_REQUEST:
     return state.set('isLoading', true);
diff --git a/app/javascript/flavours/glitch/styles/_mixins.scss b/app/javascript/flavours/glitch/styles/_mixins.scss
index 102723e39..2e317c382 100644
--- a/app/javascript/flavours/glitch/styles/_mixins.scss
+++ b/app/javascript/flavours/glitch/styles/_mixins.scss
@@ -43,10 +43,11 @@
 
 @mixin fullwidth-gallery {
   &.full-width {
-    margin-left: -22px;
-    margin-right: -22px;
+    margin-left: -14px;
+    margin-right: -14px;
     width: inherit;
     max-width: none;
     height: 250px;
+    border-radius: 0px;
   }
 }
diff --git a/app/javascript/flavours/glitch/styles/accessibility.scss b/app/javascript/flavours/glitch/styles/accessibility.scss
new file mode 100644
index 000000000..4fe5c8b1c
--- /dev/null
+++ b/app/javascript/flavours/glitch/styles/accessibility.scss
@@ -0,0 +1,13 @@
+$emojis-requiring-outlines: '8ball' 'ant' 'back' 'black_circle' 'black_heart' 'black_large_square' 'black_medium_small_square' 'black_medium_square' 'black_nib' 'black_small_square' 'bomb' 'bowling' 'bust_in_silhouette' 'busts_in_silhouette' 'camera' 'camera_with_flash' 'clubs' 'copyright' 'curly_loop' 'currency_exchange' 'dark_sunglasses' 'eight_pointed_black_star' 'electric_plug' 'end' 'female-guard' 'film_projector' 'fried_egg' 'gorilla' 'guardsman' 'heavy_check_mark' 'heavy_division_sign' 'heavy_dollar_sign' 'heavy_minus_sign' 'heavy_multiplication_x' 'heavy_plus_sign' 'hocho' 'hole' 'joystick' 'kaaba' 'lower_left_ballpoint_pen' 'lower_left_fountain_pen' 'male-guard' 'microphone' 'mortar_board' 'movie_camera' 'musical_score' 'on' 'registered' 'soon' 'spades' 'speaking_head_in_silhouette' 'spider' 'telephone_receiver' 'tm' 'top' 'tophat' 'turkey' 'vhs' 'video_camera' 'video_game' 'water_buffalo' 'waving_black_flag' 'wavy_dash' !default;
+
+%emoji-outline {
+  filter: drop-shadow(1px 1px 0 $primary-text-color) drop-shadow(-1px 1px 0 $primary-text-color) drop-shadow(1px -1px 0 $primary-text-color) drop-shadow(-1px -1px 0 $primary-text-color);
+}
+
+.emojione {
+  @each $emoji in $emojis-requiring-outlines {
+    &[title=':#{$emoji}:'] {
+      @extend %emoji-outline;
+    }
+  }
+}
diff --git a/app/javascript/flavours/glitch/styles/accounts.scss b/app/javascript/flavours/glitch/styles/accounts.scss
index ac1989832..9568581ec 100644
--- a/app/javascript/flavours/glitch/styles/accounts.scss
+++ b/app/javascript/flavours/glitch/styles/accounts.scss
@@ -267,6 +267,20 @@
     }
   }
 
+  .verified {
+    border: 1px solid rgba($valid-value-color, 0.5);
+    background: rgba($valid-value-color, 0.25);
+
+    a {
+      color: $valid-value-color;
+      font-weight: 500;
+    }
+
+    &__mark {
+      color: $valid-value-color;
+    }
+  }
+
   dl:last-child {
     border-bottom: 0;
   }
diff --git a/app/javascript/flavours/glitch/styles/admin.scss b/app/javascript/flavours/glitch/styles/admin.scss
index 7fe5e4a19..b8cc33039 100644
--- a/app/javascript/flavours/glitch/styles/admin.scss
+++ b/app/javascript/flavours/glitch/styles/admin.scss
@@ -1,3 +1,5 @@
+$no-columns-breakpoint: 600px;
+
 .admin-wrapper {
   display: flex;
   justify-content: center;
@@ -24,12 +26,22 @@
       height: 100px;
     }
 
+    @media screen and (max-width: $no-columns-breakpoint) {
+      & > a:first-child {
+        display: none;
+      }
+    }
+
     ul {
       list-style: none;
       border-radius: 4px 0 0 4px;
       overflow: hidden;
       margin-bottom: 20px;
 
+      @media screen and (max-width: $no-columns-breakpoint) {
+        margin-bottom: 0;
+      }
+
       a {
         display: block;
         padding: 15px;
@@ -62,20 +74,24 @@
         a {
           border: 0;
           padding: 15px 35px;
+        }
+      }
 
-          &.selected {
-            color: $primary-text-color;
-            background-color: $ui-highlight-color;
-            border-bottom: 0;
-            border-radius: 0;
+      .simple-navigation-active-leaf a {
+        color: $primary-text-color;
+        background-color: $ui-highlight-color;
+        border-bottom: 0;
+        border-radius: 0;
 
-            &:hover {
-              background-color: lighten($ui-highlight-color, 5%);
-            }
-          }
+        &:hover {
+          background-color: lighten($ui-highlight-color, 5%);
         }
       }
     }
+
+    & > ul > .simple-navigation-active-leaf a {
+      border-radius: 4px 0 0 4px;
+    }
   }
 
   .content-wrapper {
@@ -89,11 +105,19 @@
     padding-top: 60px;
     padding-left: 25px;
 
+    @media screen and (max-width: $no-columns-breakpoint) {
+      max-width: none;
+      padding: 15px;
+      padding-top: 30px;
+    }
+
     h2 {
       color: $secondary-text-color;
       font-size: 24px;
       line-height: 28px;
       font-weight: 400;
+      padding-bottom: 40px;
+      border-bottom: 1px solid lighten($ui-base-color, 8%);
       margin-bottom: 40px;
     }
 
@@ -108,7 +132,7 @@
     h4 {
       text-transform: uppercase;
       font-size: 13px;
-      font-weight: 500;
+      font-weight: 700;
       color: $darker-text-color;
       padding-bottom: 8px;
       margin-bottom: 8px;
@@ -122,6 +146,11 @@
       font-weight: 400;
     }
 
+    .fields-group h6 {
+      color: $primary-text-color;
+      font-weight: 500;
+    }
+
     & > p {
       font-size: 14px;
       line-height: 18px;
@@ -167,30 +196,7 @@
     }
   }
 
-  .simple_form {
-    max-width: 400px;
-
-    &.edit_user,
-    &.new_form_admin_settings,
-    &.new_form_two_factor_confirmation,
-    &.new_form_delete_confirmation,
-    &.new_import,
-    &.new_domain_block,
-    &.edit_domain_block {
-      max-width: none;
-    }
-
-    .form_two_factor_confirmation_code,
-    .form_delete_confirmation_password {
-      max-width: 400px;
-    }
-
-    .actions {
-      max-width: 400px;
-    }
-  }
-
-  @media screen and (max-width: 600px) {
+  @media screen and (max-width: $no-columns-breakpoint) {
     display: block;
     overflow-y: auto;
     -webkit-overflow-scrolling: touch;
@@ -204,16 +210,8 @@
 
     .sidebar {
       width: 100%;
-      padding: 10px 0;
+      padding: 0;
       height: auto;
-
-      .logo {
-        margin: 20px auto;
-      }
-    }
-
-    .content {
-      padding-top: 20px;
     }
   }
 }
@@ -578,3 +576,102 @@ a.name-tag,
     color: $dark-text-color;
   }
 }
+
+.report-card {
+  background: $ui-base-color;
+  border-radius: 4px;
+  margin-bottom: 20px;
+
+  &__profile {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 15px;
+
+    .account {
+      padding: 0;
+      border: 0;
+
+      &__avatar-wrapper {
+        margin-left: 0;
+      }
+    }
+
+    &__stats {
+      flex: 0 0 auto;
+      font-weight: 500;
+      color: $darker-text-color;
+      text-transform: uppercase;
+      text-align: right;
+
+      a {
+        color: inherit;
+        text-decoration: none;
+
+        &:focus,
+        &:hover,
+        &:active {
+          color: lighten($darker-text-color, 8%);
+        }
+      }
+
+      .red {
+        color: $error-value-color;
+      }
+    }
+  }
+
+  &__summary {
+    &__item {
+      display: flex;
+      justify-content: flex-start;
+      border-top: 1px solid darken($ui-base-color, 4%);
+
+      &:hover {
+        background: lighten($ui-base-color, 2%);
+      }
+
+      &__reported-by,
+      &__assigned {
+        padding: 15px;
+        flex: 0 0 auto;
+        box-sizing: border-box;
+        width: 150px;
+        color: $darker-text-color;
+
+        &,
+        .username {
+          white-space: nowrap;
+          overflow: hidden;
+          text-overflow: ellipsis;
+        }
+      }
+
+      &__content {
+        flex: 1 1 auto;
+        max-width: calc(100% - 300px);
+
+        &__icon {
+          color: $dark-text-color;
+          margin-right: 4px;
+          font-weight: 500;
+        }
+      }
+
+      &__content a {
+        display: block;
+        box-sizing: border-box;
+        width: 100%;
+        padding: 15px;
+        text-decoration: none;
+        color: $darker-text-color;
+      }
+    }
+  }
+}
+
+.one-line {
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
diff --git a/app/javascript/flavours/glitch/styles/basics.scss b/app/javascript/flavours/glitch/styles/basics.scss
index 11c91bbc9..9c2499ac4 100644
--- a/app/javascript/flavours/glitch/styles/basics.scss
+++ b/app/javascript/flavours/glitch/styles/basics.scss
@@ -1,3 +1,10 @@
+@function hex-color($color) {
+  @if type-of($color) == 'color' {
+    $color: str-slice(ie-hex-str($color), 4);
+  }
+  @return '%23' + unquote($color)
+}
+
 body {
   font-family: 'mastodon-font-sans-serif', sans-serif;
   background: darken($ui-base-color, 8%);
diff --git a/app/javascript/flavours/glitch/styles/components/accounts.scss b/app/javascript/flavours/glitch/styles/components/accounts.scss
index b2b6248ff..3eddd7fb4 100644
--- a/app/javascript/flavours/glitch/styles/components/accounts.scss
+++ b/app/javascript/flavours/glitch/styles/components/accounts.scss
@@ -239,6 +239,11 @@
   flex: 0 1 100%;
   border-left: 1px solid lighten($ui-base-color, 8%);
   padding: 10px 0;
+  border-bottom: 4px solid transparent;
+
+  &.active {
+    border-bottom: 4px solid $ui-highlight-color;
+  }
 
   & > span {
     display: block;
diff --git a/app/javascript/flavours/glitch/styles/components/boost.scss b/app/javascript/flavours/glitch/styles/components/boost.scss
index d92444042..f1ad041e9 100644
--- a/app/javascript/flavours/glitch/styles/components/boost.scss
+++ b/app/javascript/flavours/glitch/styles/components/boost.scss
@@ -1,10 +1,3 @@
-@function hex-color($color) {
-  @if type-of($color) == 'color' {
-    $color: str-slice(ie-hex-str($color), 4);
-  }
-  @return '%23' + unquote($color)
-}
-
 button.icon-button i.fa-retweet {
   background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='209'><path d='M4.97 3.16c-.1.03-.17.1-.22.18L.8 8.24c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77L5.5 3.35c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.02-2.4.02H7.1l2.32 2.85.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($action-button-color)}' stroke-width='0'/><path d='M7.78 19.66c-.24.02-.44.25-.44.5v2.46h-.06c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v4.47c0 4.26-.56 3.62 3.65 3.62H8.5l-1.3-1.06c-.1-.08-.18-.2-.2-.3-.02-.17.06-.35.2-.45l1.33-1.1H7.28c-.44 0-.72-.3-.72-.7v-4.48c0-.44.28-.72.72-.72h.06v2.5c0 .38.54.63.82.38l4.9-3.93c.25-.18.25-.6 0-.78l-4.9-3.92c-.1-.1-.24-.14-.38-.12zm9.34 2.93c-.54-.02-1.3.02-2.4.02h-1.25l1.3 1.07c.1.07.18.2.2.33.02.16-.06.3-.2.4l-1.33 1.1h1.28c.42 0 .72.28.72.72v4.47c0 .42-.3.72-.72.72h-.1v-2.47c0-.3-.3-.53-.6-.47-.07 0-.14.05-.2.1l-4.9 3.93c-.26.18-.26.6 0 .78l4.9 3.92c.27.25.82 0 .8-.38v-2.5h.1c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.15.4-3.62-1.25-3.66zM10.34 38.66c-.24.02-.44.25-.43.5v2.47H7.3c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.47c0 3.66-.23 3.7 2.34 3.66l-1.34-1.1c-.1-.08-.18-.2-.2-.3 0-.17.07-.35.2-.45l1.96-1.6c-.03-.06-.04-.13-.04-.2v-4.48c0-.44.28-.72.72-.72H9.9v2.5c0 .36.5.6.8.38l4.93-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.08-.23-.13-.36-.12zm5.63 2.93l1.34 1.1c.1.07.18.2.2.33.02.16-.03.3-.16.4l-1.96 1.6c.02.07.06.13.06.22v4.47c0 .42-.3.72-.72.72h-2.66v-2.47c0-.3-.3-.53-.6-.47-.06.02-.12.05-.18.1l-4.94 3.93c-.24.18-.24.6 0 .78l4.94 3.92c.28.22.78-.02.78-.38v-2.5h2.66c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.66.34-3.7-2.4-3.66zM13.06 57.66c-.23.03-.4.26-.4.5v2.47H7.28c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.87l2.93-2.37v-2.5c0-.44.28-.72.72-.72h5.38v2.5c0 .36.5.6.78.38l4.94-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.1-.24-.14-.38-.12zm5.3 6.15l-2.92 2.4v2.52c0 .42-.3.72-.72.72h-5.4v-2.47c0-.3-.32-.53-.6-.47-.07.02-.13.05-.2.1L3.6 70.52c-.25.18-.25.6 0 .78l4.93 3.92c.28.22.78-.02.78-.38v-2.5h5.42c4.27 0 3.65.67 3.65-3.62v-4.47-.44zM19.25 78.8c-.1.03-.2.1-.28.17l-.9.9c-.44-.3-1.36-.25-3.35-.25H7.28c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v.7l2.93.3v-1c0-.44.28-.72.72-.72h7.44c.2 0 .37.08.5.2l-1.8 1.8c-.25.26-.08.76.27.8l6.27.7c.28.03.56-.25.53-.53l-.7-6.25c0-.27-.3-.48-.55-.44zm-17.2 6.1c-.2.07-.36.3-.33.54l.7 6.25c.02.36.58.55.83.27l.8-.8c.02 0 .04-.02.04 0 .46.24 1.37.17 3.18.17h7.44c4.27 0 3.65.67 3.65-3.62v-.75l-2.93-.3v1.05c0 .42-.3.72-.72.72H7.28c-.15 0-.3-.03-.4-.1L8.8 86.4c.3-.24.1-.8-.27-.84l-6.28-.65h-.2zM4.88 98.6c-1.33 0-1.34.48-1.3 2.3l1.14-1.37c.08-.1.22-.17.34-.2.16 0 .34.08.44.2l1.66 2.03c.04 0 .07-.03.12-.03h7.44c.34 0 .57.2.65.5h-2.43c-.34.05-.53.52-.3.78l3.92 4.95c.18.24.6.24.78 0l3.94-4.94c.22-.27-.02-.76-.37-.77H18.4c.02-3.9.6-3.4-3.66-3.4H7.28c-1.08 0-1.86-.04-2.4-.04zm.15 2.46c-.1.03-.2.1-.28.2l-3.94 4.9c-.2.28.03.77.4.78H3.6c-.02 3.94-.45 3.4 3.66 3.4h7.44c3.65 0 3.74.3 3.7-2.25l-1.1 1.34c-.1.1-.2.17-.32.2-.16 0-.34-.08-.44-.2l-1.65-2.03c-.06.02-.1.04-.18.04H7.28c-.35 0-.57-.2-.66-.5h2.44c.37 0 .63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.23-.47-.2zM4.88 117.6c-1.16 0-1.3.3-1.3 1.56l1.14-1.38c.08-.1.22-.14.34-.16.16 0 .34.04.44.16l2.22 2.75h7c.42 0 .72.28.72.72v.53h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-.53c0-4.2.72-3.63-3.66-3.63H7.28c-1.08 0-1.86-.03-2.4-.03zm.1 1.74c-.1.03-.17.1-.23.16L.8 124.44c-.2.28.03.77.4.78H3.6v.5c0 4.26-.55 3.62 3.66 3.62h7.44c1.03 0 1.74.02 2.28 0-.16.02-.34-.03-.44-.15l-2.22-2.76H7.28c-.44 0-.72-.3-.72-.72v-.5h2.5c.37.02.63-.5.4-.78L5.5 119.5c-.12-.15-.34-.22-.53-.16zm12.02 10c1.2-.02 1.4-.25 1.4-1.53l-1.1 1.36c-.07.1-.17.17-.3.18zM5.94 136.6l2.37 2.93h6.42c.42 0 .72.28.72.72v1.25h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.25c0-4.2.72-3.63-3.66-3.63H7.28c-.6 0-.92-.02-1.34-.03zm-1.72.06c-.4.08-.54.3-.6.75l.6-.74zm.84.93c-.12 0-.24.08-.3.18l-3.95 4.9c-.24.3 0 .83.4.82H3.6v1.22c0 4.26-.55 3.62 3.66 3.62h7.44c.63 0 .97.02 1.4.03l-2.37-2.93H7.28c-.44 0-.72-.3-.72-.72v-1.22h2.5c.4.04.67-.53.4-.8l-3.96-4.92c-.1-.13-.27-.2-.44-.2zm13.28 10.03l-.56.7c.36-.07.5-.3.56-.7zM17.13 155.6c-.55-.02-1.32.03-2.4.03h-8.2l2.38 2.9h5.82c.42 0 .72.28.72.72v1.97H12.9c-.32.06-.48.52-.28.78l3.94 4.94c.2.23.6.22.78-.03l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.97c0-3.15.4-3.62-1.25-3.66zm-12.1.28c-.1.02-.2.1-.28.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v1.96c0 4.26-.55 3.62 3.66 3.62h8.24l-2.36-2.9H7.28c-.44 0-.72-.3-.72-.72v-1.97h2.5c.37.02.63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.22-.47-.2zM5.13 174.5c-.15 0-.3.07-.38.2L.8 179.6c-.24.27 0 .82.4.8H3.6v2.32c0 4.26-.55 3.62 3.66 3.62h7.94l-2.35-2.9h-5.6c-.43 0-.7-.3-.7-.72v-2.3h2.5c.38.03.66-.54.4-.83l-3.97-4.9c-.1-.13-.23-.2-.38-.2zm12 .1c-.55-.02-1.32.03-2.4.03H6.83l2.35 2.9h5.52c.42 0 .72.28.72.72v2.34h-2.6c-.3.1-.43.53-.2.78l3.92 4.9c.18.24.6.24.78 0l3.94-4.9c.22-.3-.02-.78-.37-.8H18.4v-2.33c0-3.15.4-3.62-1.25-3.66zM4.97 193.16c-.1.03-.17.1-.22.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77l-3.96-4.9c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.03-2.4.03H7.1l2.32 2.84.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($highlight-text-color)}' stroke-width='0'/></svg>");
 
diff --git a/app/javascript/flavours/glitch/styles/components/drawer.scss b/app/javascript/flavours/glitch/styles/components/drawer.scss
index 0432b233a..6a9af4490 100644
--- a/app/javascript/flavours/glitch/styles/components/drawer.scss
+++ b/app/javascript/flavours/glitch/styles/components/drawer.scss
@@ -5,7 +5,6 @@
   padding: 10px 5px;
   width: 300px;
   flex: none;
-  contain: strict;
 
   &:first-child {
     padding-left: 10px;
@@ -49,7 +48,6 @@
     background: lighten($ui-base-color, 13%);
     overflow-x: hidden;
     overflow-y: auto;
-    contain: strict;
 
     & > .mastodon {
       flex: 1;
@@ -253,7 +251,6 @@
   background: $ui-base-color;
   overflow-x: hidden;
   overflow-y: auto;
-  contain: strict;
 
   & > header {
     border-bottom: 1px solid darken($ui-base-color, 4%);
diff --git a/app/javascript/flavours/glitch/styles/components/index.scss b/app/javascript/flavours/glitch/styles/components/index.scss
index 36417cde8..cbf968ec4 100644
--- a/app/javascript/flavours/glitch/styles/components/index.scss
+++ b/app/javascript/flavours/glitch/styles/components/index.scss
@@ -415,14 +415,14 @@
 
   &.top {
     bottom: -5px;
-    margin-left: -13px;
+    margin-left: -7px;
     border-width: 5px 7px 0;
     border-top-color: $ui-secondary-color;
   }
 
   &.bottom {
     top: -5px;
-    margin-left: -13px;
+    margin-left: -7px;
     border-width: 0 7px 5px;
     border-bottom-color: $ui-secondary-color;
   }
@@ -571,7 +571,7 @@
     }
   }
 
-  span {
+  span:last-child {
     margin-left: 5px;
     display: none;
   }
@@ -827,6 +827,10 @@
   color: $highlight-text-color;
 }
 
+.reduce-motion button.icon-button.disabled i.fa-retweet {
+  color: darken($action-button-color, 13%);
+}
+
 .load-more {
   display: block;
   color: $dark-text-color;
@@ -837,8 +841,10 @@
   line-height: inherit;
   margin: 0;
   padding: 15px;
+  box-sizing: border-box;
   width: 100%;
   clear: both;
+  text-decoration: none;
 
   &:hover {
     background: lighten($ui-base-color, 2%);
@@ -1119,6 +1125,22 @@
   left: 0;
 }
 
+.icon-badge-wrapper {
+  position: relative;
+}
+
+.icon-badge {
+  position: absolute;
+  display: block;
+  right: -.25em;
+  top: -.25em;
+  background-color: $ui-highlight-color;
+  border-radius: 50%;
+  font-size: 75%;
+  width: 1em;
+  height: 1em;
+}
+
 ::-webkit-scrollbar-thumb {
   border-radius: 0;
 }
diff --git a/app/javascript/flavours/glitch/styles/components/media.scss b/app/javascript/flavours/glitch/styles/components/media.scss
index 5a49c07fa..40a144de4 100644
--- a/app/javascript/flavours/glitch/styles/components/media.scss
+++ b/app/javascript/flavours/glitch/styles/components/media.scss
@@ -78,17 +78,11 @@
   box-sizing: border-box;
   margin-top: 8px;
   overflow: hidden;
+  border-radius: 4px;
   position: relative;
-  background: $base-shadow-color;
   width: 100%;
   height: 110px;
 
-  .detailed-status & {
-    margin-left: -14px;
-    width: calc(100% + 28px);
-    height: 250px;
-  }
-
   @include fullwidth-gallery;
 }
 
@@ -98,6 +92,12 @@
   display: block;
   float: left;
   position: relative;
+  border-radius: 4px;
+  overflow: hidden;
+
+  .full-width & {
+    border-radius: 0;
+  }
 
   &.standalone {
     .media-gallery__item-gifv-thumbnail {
@@ -105,13 +105,17 @@
       top: 0;
     }
   }
+
+  &.letterbox {
+    background: $base-shadow-color;
+  }
 }
 
 .media-gallery__item-thumbnail {
   cursor: zoom-in;
   display: block;
   text-decoration: none;
-  height: 100%;
+  color: $secondary-text-color;
   line-height: 0;
 
   &,
@@ -294,7 +298,6 @@
     max-width: 100vw;
     max-height: 80vh;
     z-index: 1;
-    object-fit: cover;
     position: relative;
   }
 
diff --git a/app/javascript/flavours/glitch/styles/components/metadata.scss b/app/javascript/flavours/glitch/styles/components/metadata.scss
index 2efe6cd66..da045574a 100644
--- a/app/javascript/flavours/glitch/styles/components/metadata.scss
+++ b/app/javascript/flavours/glitch/styles/components/metadata.scss
@@ -4,17 +4,12 @@
   overflow: hidden;
   margin: 20px -10px -20px;
   border-bottom: 0;
-
-  a {
-    text-decoration: none;
-
-    &:hover{
-      text-decoration: underline;
-    }
-  }
+  border-top: 0;
 
   dl {
-    border-top: 1px solid lighten($ui-base-color, 8%);
+    background: $ui-base-color;
+    border-top: 1px solid lighten($ui-base-color, 4%);
+    border-bottom: 0;
     display: flex;
   }
 
@@ -35,10 +30,6 @@
     width: 120px;
     flex: 0 0 auto;
     font-weight: 500;
-
-    a {
-      color: $primary-text-color;
-    }
   }
 
   dd {
@@ -46,8 +37,9 @@
     color: $primary-text-color;
     background: $ui-base-color;
 
-    a {
-      color: $highlight-text-color;
+    &.verified {
+      border: 1px solid rgba($valid-value-color, 0.5);
+      background: rgba($valid-value-color, 0.25);
     }
   }
 }
diff --git a/app/javascript/flavours/glitch/styles/components/status.scss b/app/javascript/flavours/glitch/styles/components/status.scss
index fbc26ed2a..aa49aba55 100644
--- a/app/javascript/flavours/glitch/styles/components/status.scss
+++ b/app/javascript/flavours/glitch/styles/components/status.scss
@@ -623,7 +623,6 @@
 
 .status-card {
   display: flex;
-  cursor: pointer;
   font-size: 14px;
   border: 1px solid lighten($ui-base-color, 8%);
   border-radius: 4px;
@@ -632,20 +631,62 @@
   text-decoration: none;
   overflow: hidden;
 
-  &:hover {
-    background: lighten($ui-base-color, 8%);
+  &__actions {
+    bottom: 0;
+    left: 0;
+    position: absolute;
+    right: 0;
+    top: 0;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+
+    & > div {
+      background: rgba($base-shadow-color, 0.6);
+      border-radius: 4px;
+      padding: 12px 9px;
+      flex: 0 0 auto;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+    }
+
+    button,
+    a {
+      display: inline;
+      color: $primary-text-color;
+      background: transparent;
+      border: 0;
+      padding: 0 5px;
+      text-decoration: none;
+      opacity: 0.6;
+      font-size: 18px;
+      line-height: 18px;
+
+      &:hover,
+      &:active,
+      &:focus {
+        opacity: 1;
+      }
+    }
+
+    a {
+      font-size: 19px;
+      position: relative;
+      bottom: -1px;
+    }
+
+    a .fa, a:hover .fa {
+      color: inherit;
+    }
   }
 }
 
-.status-card-video,
-.status-card-rich,
-.status-card-photo {
-  margin-top: 14px;
-  overflow: hidden;
+a.status-card {
+  cursor: pointer;
 
-  iframe {
-    width: 100%;
-    height: auto;
+  &:hover {
+    background: lighten($ui-base-color, 8%);
   }
 }
 
@@ -673,6 +714,7 @@
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
+  text-decoration: none;
 }
 
 .status-card__content {
@@ -694,6 +736,7 @@
 .status-card__image {
   flex: 0 0 100px;
   background: lighten($ui-base-color, 8%);
+  position: relative;
 }
 
 .status-card.horizontal {
@@ -719,6 +762,8 @@
   width: 100%;
   height: 100%;
   object-fit: cover;
+  background-size: cover;
+  background-position: center center;
 }
 
 .status__video-player {
@@ -736,7 +781,7 @@
 
 .status__video-player-video {
   height: 100%;
-  object-fit: cover;
+  object-fit: contain;
   position: relative;
   top: 50%;
   transform: translateY(-50%);
diff --git a/app/javascript/flavours/glitch/styles/containers.scss b/app/javascript/flavours/glitch/styles/containers.scss
index b5d79f4d7..d1b9934d7 100644
--- a/app/javascript/flavours/glitch/styles/containers.scss
+++ b/app/javascript/flavours/glitch/styles/containers.scss
@@ -347,6 +347,23 @@
     margin-bottom: 10px;
     box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
 
+    &.inactive {
+      opacity: 0.5;
+
+      .public-account-header__image,
+      .avatar {
+        filter: grayscale(100%);
+      }
+
+      .logo-button {
+        background-color: $secondary-text-color;
+
+        svg path:last-child {
+          fill: $secondary-text-color;
+        }
+      }
+    }
+
     &__image {
       border-radius: 4px 4px 0 0;
       overflow: hidden;
@@ -588,6 +605,10 @@
               border-bottom: 4px solid $highlight-text-color;
               opacity: 1;
             }
+
+            &.inactive::after {
+              border-bottom-color: $secondary-text-color;
+            }
           }
 
           &:hover {
@@ -703,6 +724,14 @@
       a {
         color: lighten($ui-highlight-color, 8%);
       }
+
+      dl:first-child .verified {
+        border-radius: 0 4px 0 0;
+      }
+
+      .verified a {
+        color: $valid-value-color;
+      }
     }
 
     .account__header__content {
diff --git a/app/javascript/flavours/glitch/styles/forms.scss b/app/javascript/flavours/glitch/styles/forms.scss
index f97890187..cbd3de94c 100644
--- a/app/javascript/flavours/glitch/styles/forms.scss
+++ b/app/javascript/flavours/glitch/styles/forms.scss
@@ -1,3 +1,5 @@
+$no-columns-breakpoint: 600px;
+
 code {
   font-family: 'mastodon-font-monospace', monospace;
   font-weight: 400;
@@ -13,6 +15,60 @@ code {
   .input {
     margin-bottom: 15px;
     overflow: hidden;
+
+    &.hidden {
+      margin: 0;
+    }
+
+    &.radio_buttons {
+      .radio {
+        margin-bottom: 15px;
+
+        &:last-child {
+          margin-bottom: 0;
+        }
+      }
+
+      .radio > label {
+        position: relative;
+        padding-left: 28px;
+
+        input {
+          position: absolute;
+          top: -2px;
+          left: 0;
+        }
+      }
+    }
+
+    &.boolean {
+      position: relative;
+      margin-bottom: 0;
+
+      .label_input > label {
+        font-family: inherit;
+        font-size: 14px;
+        padding-top: 5px;
+        color: $primary-text-color;
+        display: block;
+        width: auto;
+      }
+
+      .label_input,
+      .hint {
+        padding-left: 28px;
+      }
+
+      .label_input__wrapper {
+        position: static;
+      }
+
+      label.checkbox {
+        position: absolute;
+        top: 2px;
+        left: 0;
+      }
+    }
   }
 
   .row {
@@ -27,9 +83,22 @@ code {
     }
   }
 
+  .hint {
+    color: $darker-text-color;
+
+    a {
+      color: $highlight-text-color;
+    }
+
+    code {
+      border-radius: 3px;
+      padding: 0.2em 0.4em;
+      background: darken($ui-base-color, 12%);
+    }
+  }
+
   span.hint {
     display: block;
-    color: $darker-text-color;
     font-size: 12px;
     margin-top: 4px;
   }
@@ -44,11 +113,6 @@ code {
       line-height: 18px;
       margin-top: 15px;
       margin-bottom: 0;
-      color: $darker-text-color;
-
-      a {
-        color: $highlight-text-color;
-      }
     }
   }
 
@@ -66,81 +130,60 @@ code {
     }
   }
 
-  .label_input {
-    display: flex;
+  .input.with_floating_label {
+    .label_input {
+      display: flex;
 
-    label {
-      flex: 0 0 auto;
+      & > label {
+        font-family: inherit;
+        font-size: 14px;
+        color: $primary-text-color;
+        font-weight: 500;
+        min-width: 150px;
+        flex: 0 0 auto;
+      }
+
+      input,
+      select {
+        flex: 1 1 auto;
+      }
     }
 
-    input {
-      flex: 1 1 auto;
+    &.select .hint {
+      margin-top: 6px;
+      margin-left: 150px;
     }
   }
 
   .input.with_label {
-    padding: 15px 0;
-    margin-bottom: 0;
-
-    .label_input {
-      flex-wrap: wrap;
-      align-items: flex-start;
-    }
-
-    &.select .label_input {
-      align-items: initial;
-    }
-
     .label_input > label {
       font-family: inherit;
-      font-size: 16px;
+      font-size: 14px;
       color: $primary-text-color;
       display: block;
-      padding-top: 5px;
-      margin-bottom: 5px;
-      flex: 1;
-      min-width: 150px;
+      margin-bottom: 8px;
       word-wrap: break-word;
+      font-weight: 500;
+    }
 
-      &.select {
-        flex: 0;
-      }
-
-      & ~ * {
-        margin-left: 10px;
-      }
+    .hint {
+      margin-top: 6px;
     }
 
     ul {
       flex: 390px;
     }
-
-    &.boolean {
-      padding: initial;
-      margin-bottom: initial;
-
-      .label_input > label {
-        font-family: inherit;
-        font-size: 14px;
-        color: $primary-text-color;
-        display: block;
-        width: auto;
-      }
-
-      label.checkbox {
-        position: relative;
-        padding-left: 25px;
-        flex: 1 1 auto;
-      }
-    }
   }
 
   .input.with_block_label {
+    max-width: none;
+
     & > label {
       font-family: inherit;
       font-size: 16px;
       color: $primary-text-color;
       display: block;
+      font-weight: 500;
       padding-top: 5px;
     }
 
@@ -148,55 +191,75 @@ code {
       margin-bottom: 15px;
     }
 
-    li {
-      float: left;
-      width: 50%;
+    ul {
+      columns: 2;
     }
   }
 
+  .required abbr {
+    text-decoration: none;
+    color: lighten($error-value-color, 12%);
+  }
+
   .fields-group {
     margin-bottom: 25px;
-  }
 
-  .input.radio_buttons .radio label {
-    margin-bottom: 5px;
-    font-family: inherit;
-    font-size: 14px;
-    color: $primary-text-color;
-    display: block;
-    width: auto;
+    .input:last-child {
+      margin-bottom: 0;
+    }
   }
 
-  .input.boolean {
-    margin-bottom: 5px;
+  .fields-row {
+    display: flex;
+    margin: 0 -10px;
+    padding-top: 5px;
+    margin-bottom: 25px;
 
-    label {
-      font-family: inherit;
-      font-size: 14px;
-      color: $primary-text-color;
-      display: block;
-      width: auto;
+    .input {
+      max-width: none;
     }
 
-    label.checkbox {
-      position: relative;
-      padding-left: 25px;
+    &__column {
+      box-sizing: border-box;
+      padding: 0 10px;
       flex: 1 1 auto;
+      min-height: 1px;
+
+      &-6 {
+        max-width: 50%;
+      }
     }
 
-    input[type=checkbox] {
-      position: absolute;
-      left: 0;
-      top: 5px;
-      margin: 0;
+    .fields-group:last-child,
+    .fields-row__column.fields-group {
+      margin-bottom: 0;
     }
 
-    .hint {
-      padding-left: 25px;
-      margin-left: 0;
+    @media screen and (max-width: $no-columns-breakpoint) {
+      display: block;
+      margin-bottom: 0;
+
+      &__column {
+        max-width: none;
+      }
+
+      .fields-group:last-child,
+      .fields-row__column.fields-group,
+      .fields-row__column {
+        margin-bottom: 25px;
+      }
     }
   }
 
+  .input.radio_buttons .radio label {
+    margin-bottom: 5px;
+    font-family: inherit;
+    font-size: 14px;
+    color: $primary-text-color;
+    display: block;
+    width: auto;
+  }
+
   .check_boxes {
     .checkbox {
       label {
@@ -225,12 +288,7 @@ code {
   input[type=email],
   input[type=password],
   textarea {
-    background: transparent;
     box-sizing: border-box;
-    border: 0;
-    border-bottom: 2px solid $ui-primary-color;
-    border-radius: 2px 2px 0 0;
-    padding: 7px 4px;
     font-size: 16px;
     color: $primary-text-color;
     display: block;
@@ -238,23 +296,31 @@ code {
     outline: 0;
     font-family: inherit;
     resize: vertical;
+    background: darken($ui-base-color, 10%);
+    border: 1px solid darken($ui-base-color, 14%);
+    border-radius: 4px;
+    padding: 10px;
 
     &:invalid {
       box-shadow: none;
     }
 
     &:focus:invalid {
-      border-bottom-color: lighten($error-red, 12%);
+      border-color: lighten($error-red, 12%);
     }
 
     &:required:valid {
-      border-bottom-color: $valid-value-color;
+      border-color: $valid-value-color;
+    }
+
+    &:hover {
+      border-color: darken($ui-base-color, 20%);
     }
 
     &:active,
     &:focus {
-      border-bottom-color: $highlight-text-color;
-      background: rgba($base-overlay-background, 0.1);
+      border-color: $highlight-text-color;
+      background: darken($ui-base-color, 8%);
     }
   }
 
@@ -338,28 +404,52 @@ code {
   }
 
   select {
+    appearance: none;
+    box-sizing: border-box;
     font-size: 16px;
-    max-height: 29px;
+    color: $primary-text-color;
+    display: block;
+    width: 100%;
+    outline: 0;
+    font-family: inherit;
+    resize: vertical;
+    background: darken($ui-base-color, 10%) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(lighten($ui-base-color, 12%))}'/></svg>") no-repeat right 8px center / auto 16px;
+    border: 1px solid darken($ui-base-color, 14%);
+    border-radius: 4px;
+    padding: 10px;
+    height: 41px;
   }
 
-  .input-with-append {
-    position: relative;
-
-    .input input {
-      padding-right: 127px;
+  .label_input {
+    &__wrapper {
+      position: relative;
     }
 
-    .append {
+    &__append {
       position: absolute;
-      right: 0;
-      top: 0;
-      padding: 7px 4px;
+      right: 1px;
+      top: 1px;
+      padding: 10px;
       padding-bottom: 9px;
       font-size: 16px;
       color: $dark-text-color;
       font-family: inherit;
       pointer-events: none;
       cursor: default;
+      max-width: 140px;
+      white-space: nowrap;
+      overflow: hidden;
+
+      &::after {
+        content: '';
+        display: block;
+        position: absolute;
+        top: 0;
+        right: 0;
+        bottom: 1px;
+        width: 5px;
+        background-image: linear-gradient(to right, rgba(darken($ui-base-color, 10%), 0), darken($ui-base-color, 10%));
+      }
     }
   }
 }
@@ -434,6 +524,30 @@ code {
   }
 }
 
+.quick-nav {
+  list-style: none;
+  margin-bottom: 25px;
+  font-size: 14px;
+
+  li {
+    display: inline-block;
+    margin-right: 10px;
+  }
+
+  a {
+    color: $highlight-text-color;
+    text-transform: uppercase;
+    text-decoration: none;
+    font-weight: 700;
+
+    &:hover,
+    &:focus,
+    &:active {
+      color: lighten($highlight-text-color, 8%);
+    }
+  }
+}
+
 .oauth-prompt,
 .follow-prompt {
   margin-bottom: 30px;
@@ -592,3 +706,64 @@ code {
     display: block;
   }
 }
+
+.scope-danger {
+  color: $warning-red;
+}
+
+.form_admin_settings_site_short_description,
+.form_admin_settings_site_description,
+.form_admin_settings_site_extended_description,
+.form_admin_settings_site_terms,
+.form_admin_settings_custom_css,
+.form_admin_settings_closed_registrations_message {
+  textarea {
+    font-family: 'mastodon-font-monospace', monospace;
+  }
+}
+
+.input-copy {
+  background: darken($ui-base-color, 10%);
+  border: 1px solid darken($ui-base-color, 14%);
+  border-radius: 4px;
+  display: flex;
+  align-items: center;
+  padding-right: 4px;
+  position: relative;
+  top: 1px;
+  transition: border-color 300ms linear;
+
+  &__wrapper {
+    flex: 1 1 auto;
+  }
+
+  input[type=text] {
+    background: transparent;
+    border: 0;
+    padding: 10px;
+    font-size: 14px;
+    font-family: 'mastodon-font-monospace', monospace;
+  }
+
+  button {
+    flex: 0 0 auto;
+    margin: 4px;
+    text-transform: none;
+    font-weight: 400;
+    font-size: 14px;
+    padding: 7px 18px;
+    padding-bottom: 6px;
+    width: auto;
+    transition: background 300ms linear;
+  }
+
+  &.copied {
+    border-color: $valid-value-color;
+    transition: none;
+
+    button {
+      background: $valid-value-color;
+      transition: none;
+    }
+  }
+}
diff --git a/app/javascript/flavours/glitch/styles/index.scss b/app/javascript/flavours/glitch/styles/index.scss
index 8e3ff43e3..3cb592499 100644
--- a/app/javascript/flavours/glitch/styles/index.scss
+++ b/app/javascript/flavours/glitch/styles/index.scss
@@ -19,5 +19,6 @@
 @import 'about';
 @import 'tables';
 @import 'admin';
+@import 'accessibility';
 @import 'rtl';
 @import 'dashboard';
diff --git a/app/javascript/flavours/glitch/styles/mastodon-light.scss b/app/javascript/flavours/glitch/styles/mastodon-light.scss
index 029d5dde2..8fc132651 100644
--- a/app/javascript/flavours/glitch/styles/mastodon-light.scss
+++ b/app/javascript/flavours/glitch/styles/mastodon-light.scss
@@ -1,218 +1,3 @@
-// Set variables
-$ui-base-color: #d9e1e8;
-$ui-base-lighter-color: darken($ui-base-color, 57%);
-$ui-highlight-color: #2b90d9;
-$ui-primary-color: darken($ui-highlight-color, 28%);
-$ui-secondary-color: #282c37;
-
-$primary-text-color: black;
-$base-overlay-background: $ui-base-color;
-
-$login-button-color: white;
-$account-background-color: white;
-
-// Import defaults
+@import 'mastodon-light/variables';
 @import 'index';
-
-// Change the color of the log in button
-.button {
-  &.button-alternative-2 {
-    color: $login-button-color;
-  }
-}
-
-// Change columns' default background colors
-.column {
-  > .scrollable {
-    background: lighten($ui-base-color, 13%);
-  }
-}
-
-.status.collapsed .status__content:after {
-  background: linear-gradient(rgba(lighten($ui-base-color, 13%), 0), rgba(lighten($ui-base-color, 13%), 1));
-}
-
-.drawer__inner {
-  background: $ui-base-color;
-}
-
-.drawer > .contents {
-  background: $ui-base-color url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 234.80078 31.757813" width="234.80078" height="31.757812"><path d="M19.599609 0c-1.05 0-2.10039.375-2.90039 1.125L0 16.925781v14.832031h234.80078V17.025391l-16.5-15.900391c-1.6-1.5-4.20078-1.5-5.80078 0l-13.80078 13.099609c-1.6 1.5-4.19883 1.5-5.79883 0L179.09961 1.125c-1.6-1.5-4.19883-1.5-5.79883 0L159.5 14.224609c-1.6 1.5-4.20078 1.5-5.80078 0L139.90039 1.125c-1.6-1.5-4.20078-1.5-5.80078 0l-13.79883 13.099609c-1.6 1.5-4.20078 1.5-5.80078 0L100.69922 1.125c-1.600001-1.5-4.198829-1.5-5.798829 0l-13.59961 13.099609c-1.6 1.5-4.200781 1.5-5.800781 0L61.699219 1.125c-1.6-1.5-4.198828-1.5-5.798828 0L42.099609 14.224609c-1.6 1.5-4.198828 1.5-5.798828 0L22.5 1.125C21.7.375 20.649609 0 19.599609 0z" fill="#{hex-color(lighten($ui-base-color, 13%))}"/></svg>') no-repeat bottom / 100% auto !important;
-
-  .mastodon {
-    filter: contrast(75%) brightness(75%) !important;
-  }
-}
-
-// Change the default appearance of the content warning button
-.status__content {
-
-  .status__content__spoiler-link {
-
-    background: darken($ui-base-color, 30%);
-
-    &:hover {
-      background: darken($ui-base-color, 35%);
-      text-decoration: none;
-    }
-
-  }
-
-}
-
-// Change the default appearance of the action buttons
-.icon-button {
-
-  &:hover,
-  &:active,
-  &:focus {
-    color: darken($ui-base-color, 40%);
-    transition: color 200ms ease-out;
-  }
-
-  &.disabled {
-    color: darken($ui-base-color, 30%);
-  }
-
-}
-
-.status {
-  &.status-direct {
-    .icon-button.disabled {
-      color: darken($ui-base-color, 30%);
-    }
-  }
-}
-
-// Change the colors used in the dropdown menu
-.dropdown-menu {
-  background: $ui-base-color;
-}
-
-.dropdown-menu__arrow {
-
-  &.left {
-    border-left-color: $ui-base-color;
-  }
-
-  &.top {
-    border-top-color: $ui-base-color;
-  }
-
-  &.bottom {
-    border-bottom-color: $ui-base-color;
-  }
-
-  &.right {
-    border-right-color: $ui-base-color;
-  }
-
-}
-
-.dropdown-menu__item {
-  a {
-    background: $ui-base-color;
-    color: $ui-secondary-color;
-  }
-}
-
-// Change the default color of several parts of the compose form
-.composer {
-
-  .composer--spoiler input, .composer--textarea textarea {
-    color: darken($ui-base-color, 80%);
-
-    &:disabled { background: darken($simple-background-color, 10%) }
-
-    &::placeholder {
-      color: darken($ui-base-color, 70%);
-    }
-  }
-
-  strong {
-    color: lighten($ui-secondary-color, 65%);
-  }
-
-  .composer--options {
-    background: darken($ui-base-color, 10%);
-    box-shadow: unset;
-  }
-
-  .composer--options--dropdown--content--item {
-    color: $ui-primary-color;
-    
-    strong {
-      color: $ui-primary-color;
-    }
-
-  }
-
-}
-
-// Change the default color used for the text in an empty column or on the error column
-.empty-column-indicator,
-.error-column {
-  color: darken($ui-base-color, 60%);
-}
-
-// Change the default colors used on some parts of the profile pages
-.activity-stream-tabs {
-
-  background: $account-background-color;
-
-  a {
-    &.active {
-      color: $ui-primary-color;
-      }
-  }
-
-}
-
-.activity-stream {
-
-  .entry {
-    background: $account-background-color;
-  }
-
-  .status.light {
-
-    .status__content {
-      color: $primary-text-color;
-    }
-
-    .display-name {
-      strong {
-        color: $primary-text-color;
-      }
-    }
-
-  }
-
-}
-
-.accounts-grid {
-  .account-grid-card {
-
-    .controls {
-      .icon-button {
-        color: $ui-secondary-color;
-      }
-    }
-
-    .name {
-      a {
-        color: $primary-text-color;
-      }
-    }
-
-    .username {
-      color: $ui-secondary-color;
-    }
-
-    .account__header__content {
-      color: $primary-text-color;
-    }
-
-  }
-}
-
+@import 'mastodon-light/diff';
diff --git a/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss b/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss
new file mode 100644
index 000000000..aba8baf70
--- /dev/null
+++ b/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss
@@ -0,0 +1,318 @@
+// Notes!
+// Sass color functions, "darken" and "lighten" are automatically replaced.
+
+.glitch.local-settings {
+  background: $ui-base-color;
+
+  &__navigation {
+    background: darken($ui-base-color, 8%);
+  }
+
+  &__navigation__item {
+    background: darken($ui-base-color, 8%);
+
+    &:hover {
+      background: $ui-base-color;
+    }
+  }
+}
+
+.notification__dismiss-overlay {
+  .wrappy {
+    box-shadow: unset;
+  }
+
+  .ckbox {
+    text-shadow: unset;
+  }
+}
+
+.status.status-direct {
+  background: darken($ui-base-color, 8%);
+
+  &.collapsed> .status__content:after {
+    background: linear-gradient(rgba(darken($ui-base-color, 8%), 0), rgba(darken($ui-base-color, 8%), 1));
+  }
+}
+
+.focusable:focus.status.status-direct {
+  background: darken($ui-base-color, 4%);
+
+  &.collapsed> .status__content:after {
+    background: linear-gradient(rgba(darken($ui-base-color, 4%), 0), rgba(darken($ui-base-color, 4%), 1));
+  }
+}
+
+// Change columns' default background colors
+.column {
+  > .scrollable {
+    background: darken($ui-base-color, 13%);
+  }
+}
+
+.status.collapsed .status__content:after {
+  background: linear-gradient(rgba(darken($ui-base-color, 13%), 0), rgba(darken($ui-base-color, 13%), 1));
+}
+
+.drawer__inner {
+  background: $ui-base-color;
+}
+
+.drawer > .contents {
+  background: $ui-base-color url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 234.80078 31.757813" width="234.80078" height="31.757812"><path d="M19.599609 0c-1.05 0-2.10039.375-2.90039 1.125L0 16.925781v14.832031h234.80078V17.025391l-16.5-15.900391c-1.6-1.5-4.20078-1.5-5.80078 0l-13.80078 13.099609c-1.6 1.5-4.19883 1.5-5.79883 0L179.09961 1.125c-1.6-1.5-4.19883-1.5-5.79883 0L159.5 14.224609c-1.6 1.5-4.20078 1.5-5.80078 0L139.90039 1.125c-1.6-1.5-4.20078-1.5-5.80078 0l-13.79883 13.099609c-1.6 1.5-4.20078 1.5-5.80078 0L100.69922 1.125c-1.600001-1.5-4.198829-1.5-5.798829 0l-13.59961 13.099609c-1.6 1.5-4.200781 1.5-5.800781 0L61.699219 1.125c-1.6-1.5-4.198828-1.5-5.798828 0L42.099609 14.224609c-1.6 1.5-4.198828 1.5-5.798828 0L22.5 1.125C21.7.375 20.649609 0 19.599609 0z" fill="#{hex-color(darken($ui-base-color, 13%))}"/></svg>') no-repeat bottom / 100% auto !important;
+
+  .mastodon {
+    filter: contrast(75%) brightness(75%) !important;
+  }
+}
+
+// Change the default appearance of the content warning button
+.status__content {
+
+  .status__content__spoiler-link {
+
+    background: lighten($ui-base-color, 30%);
+
+    &:hover {
+      background: lighten($ui-base-color, 35%);
+      text-decoration: none;
+    }
+
+  }
+
+}
+
+// Change the background colors of media and video spoilers
+.media-spoiler,
+.video-player__spoiler {
+  background: $ui-base-color;
+}
+
+// Change the colors used in the dropdown menu
+.dropdown-menu {
+  background: $ui-base-color;
+}
+
+.dropdown-menu__arrow {
+
+  &.left {
+    border-left-color: $ui-base-color;
+  }
+
+  &.top {
+    border-top-color: $ui-base-color;
+  }
+
+  &.bottom {
+    border-bottom-color: $ui-base-color;
+  }
+
+  &.right {
+    border-right-color: $ui-base-color;
+  }
+
+}
+
+.dropdown-menu__item {
+  a {
+    background: $ui-base-color;
+    color: $ui-secondary-color;
+  }
+}
+
+// Change the default color of several parts of the compose form
+.composer {
+
+  .composer--spoiler input, .composer--textarea textarea {
+    color: lighten($ui-base-color, 80%);
+
+    &:disabled { background: lighten($simple-background-color, 10%) }
+
+    &::placeholder {
+      color: lighten($ui-base-color, 70%);
+    }
+  }
+
+  .composer--options {
+    background: lighten($ui-base-color, 10%);
+    box-shadow: unset;
+
+    & > hr {
+      display: none;
+    }
+  }
+
+  .composer--options--dropdown--content--item {
+    color: $ui-primary-color;
+    
+    strong {
+      color: $ui-primary-color;
+    }
+
+  }
+
+}
+
+.composer--upload_form--actions .icon-button {
+  color: lighten($white, 7%);
+
+  &:active,
+  &:focus,
+  &:hover {
+    color: $white;
+  }
+}
+
+.composer--upload_form--item > div input {
+  color: lighten($white, 7%);
+
+  &::placeholder {
+    color: lighten($white, 10%);
+  }
+}
+
+.dropdown-menu__separator {
+  border-bottom-color: lighten($ui-base-color, 12%);
+}
+
+.status__content,
+.reply-indicator__content {
+  a {
+    color: $highlight-text-color;
+  }
+}
+
+.emoji-mart-bar {
+  border-color: darken($ui-base-color, 4%);
+
+  &:first-child {
+    background: lighten($ui-base-color, 10%);
+  }
+}
+
+.emoji-mart-search input {
+  background: rgba($ui-base-color, 0.3);
+  border-color: $ui-base-color;
+}
+
+.composer--textarea--suggestions {
+  background: lighten($ui-base-color, 10%)
+}
+
+.composer--textarea--suggestions--item {
+  &:hover,
+  &:focus,
+  &:active,
+  &.selected {
+    background: darken($ui-base-color, 4%);
+  }
+}
+
+.react-toggle-track {
+  background: $ui-secondary-color;
+}
+
+.react-toggle:hover:not(.react-toggle--disabled) .react-toggle-track {
+  background: lighten($ui-secondary-color, 10%);
+}
+
+.react-toggle.react-toggle--checked:hover:not(.react-toggle--disabled) .react-toggle-track {
+  background: darken($ui-highlight-color, 10%);
+}
+
+// Change the background colors of modals
+.actions-modal,
+.boost-modal,
+.confirmation-modal,
+.mute-modal,
+.report-modal,
+.embed-modal,
+.error-modal,
+.onboarding-modal {
+  background: $ui-base-color;
+}
+
+.boost-modal__action-bar,
+.confirmation-modal__action-bar,
+.mute-modal__action-bar,
+.onboarding-modal__paginator,
+.error-modal__footer {
+  background: darken($ui-base-color, 6%);
+
+  .onboarding-modal__nav,
+  .error-modal__nav {
+    &:hover,
+    &:focus,
+    &:active {
+      background-color: darken($ui-base-color, 12%);
+    }
+  }
+}
+
+// Change the default color used for the text in an empty column or on the error column
+.empty-column-indicator,
+.error-column {
+  color: lighten($ui-base-color, 60%);
+}
+
+// Change the default colors used on some parts of the profile pages
+.activity-stream-tabs {
+
+  background: $account-background-color;
+
+  a {
+    &.active {
+      color: $ui-primary-color;
+      }
+  }
+
+}
+
+.activity-stream {
+
+  .entry {
+    background: $account-background-color;
+  }
+
+  .status.light {
+
+    .status__content {
+      color: $primary-text-color;
+    }
+
+    .display-name {
+      strong {
+        color: $primary-text-color;
+      }
+    }
+
+  }
+
+}
+
+.accounts-grid {
+  .account-grid-card {
+
+    .controls {
+      .icon-button {
+        color: $ui-secondary-color;
+      }
+    }
+
+    .name {
+      a {
+        color: $primary-text-color;
+      }
+    }
+
+    .username {
+      color: $ui-secondary-color;
+    }
+
+    .account__header__content {
+      color: $primary-text-color;
+    }
+
+  }
+}
+
diff --git a/app/javascript/flavours/glitch/styles/mastodon-light/variables.scss b/app/javascript/flavours/glitch/styles/mastodon-light/variables.scss
new file mode 100644
index 000000000..1b060b58d
--- /dev/null
+++ b/app/javascript/flavours/glitch/styles/mastodon-light/variables.scss
@@ -0,0 +1,38 @@
+// Dependent colors
+$black: #000000;
+$white: #ffffff;
+
+$classic-base-color: #282c37;
+$classic-primary-color: #9baec8;
+$classic-secondary-color: #d9e1e8;
+$classic-highlight-color: #2b90d9;
+
+$ui-base-color: $classic-secondary-color !default;
+$ui-base-lighter-color: darken($ui-base-color, 57%);
+$ui-highlight-color: $classic-highlight-color !default;
+$ui-primary-color: $classic-primary-color !default;
+$ui-secondary-color: $classic-base-color !default;
+
+$primary-text-color: $black !default;
+$darker-text-color: $classic-base-color !default;
+$dark-text-color: #444b5d;
+$action-button-color: #606984;
+
+$base-overlay-background: $white !default;
+
+$inverted-text-color: $black !default;
+$lighter-text-color: $classic-base-color !default;
+$light-text-color: #444b5d;
+
+$account-background-color: $white !default;
+
+//Invert darkened and lightened colors
+@function darken($color, $amount) {
+  @return hsl(hue($color), saturation($color), lightness($color) + $amount);
+}
+
+@function lighten($color, $amount) {
+  @return hsl(hue($color), saturation($color), lightness($color) - $amount);
+}
+
+$emojis-requiring-outlines: 'alien' 'baseball' 'chains' 'chicken' 'cloud' 'crescent_moon' 'dash' 'dove_of_peace' 'eyes' 'first_quarter_moon' 'first_quarter_moon_with_face' 'fish_cake' 'full_moon' 'full_moon_with_face' 'ghost' 'goat' 'grey_exclamation' 'grey_question' 'ice_skate' 'last_quarter_moon' 'last_quarter_moon_with_face' 'lightning' 'loud_sound' 'moon' 'mute' 'page_with_curl' 'rain_cloud' 'ram' 'rice' 'rice_ball' 'rooster' 'sheep' 'skull' 'skull_and_crossbones' 'snow_cloud' 'sound' 'speaker' 'speech_balloon' 'thought_balloon' 'volleyball' 'waning_crescent_moon' 'waning_gibbous_moon' 'waving_white_flag' 'waxing_crescent_moon' 'white_circle' 'white_large_square' 'white_medium_small_square' 'white_medium_square' 'white_small_square' 'wind_blowing_face';
diff --git a/app/javascript/flavours/glitch/styles/rtl.scss b/app/javascript/flavours/glitch/styles/rtl.scss
index e9099a9e9..b8c0efad8 100644
--- a/app/javascript/flavours/glitch/styles/rtl.scss
+++ b/app/javascript/flavours/glitch/styles/rtl.scss
@@ -206,13 +206,19 @@ body.rtl {
   }
 
   .simple_form .input-with-append .input input {
-    padding-left: 127px;
+    padding-left: 142px;
     padding-right: 0;
   }
 
   .simple_form .input-with-append .append {
     right: auto;
     left: 0;
+
+    &::after {
+      right: auto;
+      left: 0;
+      background-image: linear-gradient(to left, rgba($ui-base-color, 0), $ui-base-color);
+    }
   }
 
   .table th,
diff --git a/app/javascript/flavours/glitch/styles/stream_entries.scss b/app/javascript/flavours/glitch/styles/stream_entries.scss
index cf82be073..c458fcb79 100644
--- a/app/javascript/flavours/glitch/styles/stream_entries.scss
+++ b/app/javascript/flavours/glitch/styles/stream_entries.scss
@@ -3,6 +3,7 @@
   border-radius: 4px;
   overflow: hidden;
   margin-bottom: 10px;
+  text-align: left;
 
   @media screen and (max-width: $no-gap-breakpoint) {
     margin-bottom: 0;
@@ -36,7 +37,8 @@
 
     &:last-child {
       .detailed-status,
-      .status {
+      .status,
+      .load-more {
         border-bottom: 0;
         border-radius: 0 0 4px 4px;
       }
@@ -44,13 +46,15 @@
 
     &:first-child {
       .detailed-status,
-      .status {
+      .status,
+      .load-more {
         border-radius: 4px 4px 0 0;
       }
 
       &:last-child {
         .detailed-status,
-        .status {
+        .status,
+        .load-more {
           border-radius: 4px;
         }
       }
@@ -58,11 +62,16 @@
 
     @media screen and (max-width: 740px) {
       .detailed-status,
-      .status {
+      .status,
+      .load-more {
         border-radius: 0 !important;
       }
     }
   }
+
+  &--highlighted .entry {
+    background: lighten($ui-base-color, 8%);
+  }
 }
 
 .button.logo-button {
@@ -101,6 +110,18 @@
     }
   }
 
+  &.button--destructive {
+    &:active,
+    &:focus,
+    &:hover {
+      background: $error-red;
+
+      svg path:last-child {
+        fill: $error-red;
+      }
+    }
+  }
+
   @media screen and (max-width: $no-gap-breakpoint) {
     svg {
       display: none;
diff --git a/app/javascript/flavours/glitch/util/emoji/index.js b/app/javascript/flavours/glitch/util/emoji/index.js
index c6416db2d..82a1ef89c 100644
--- a/app/javascript/flavours/glitch/util/emoji/index.js
+++ b/app/javascript/flavours/glitch/util/emoji/index.js
@@ -62,6 +62,10 @@ const emojify = (str, customEmojis = {}) => {
       const title = shortCode ? `:${shortCode}:` : '';
       replacement = `<img draggable="false" class="emojione" alt="${match}" title="${title}" src="${assetHost}/emoji/${filename}.svg" />`;
       rend = i + match.length;
+      // If the matched character was followed by VS15 (for selecting text presentation), skip it.
+      if (str.codePointAt(rend) === 65038) {
+        rend += 1;
+      }
     }
     rtn += str.slice(0, i) + replacement;
     str = str.slice(rend);
diff --git a/app/javascript/flavours/glitch/util/initial_state.js b/app/javascript/flavours/glitch/util/initial_state.js
index 236ea1c3a..fdf004527 100644
--- a/app/javascript/flavours/glitch/util/initial_state.js
+++ b/app/javascript/flavours/glitch/util/initial_state.js
@@ -21,5 +21,6 @@ export const deleteModal = getMeta('delete_modal');
 export const me = getMeta('me');
 export const searchEnabled = getMeta('search_enabled');
 export const maxChars = (initialState && initialState.max_toot_chars) || 500;
+export const isStaff = getMeta('is_staff');
 
 export default initialState;
diff --git a/app/javascript/mastodon/actions/dropdown_menu.js b/app/javascript/mastodon/actions/dropdown_menu.js
index 217ba4e74..14f2939c7 100644
--- a/app/javascript/mastodon/actions/dropdown_menu.js
+++ b/app/javascript/mastodon/actions/dropdown_menu.js
@@ -1,8 +1,8 @@
 export const DROPDOWN_MENU_OPEN = 'DROPDOWN_MENU_OPEN';
 export const DROPDOWN_MENU_CLOSE = 'DROPDOWN_MENU_CLOSE';
 
-export function openDropdownMenu(id, placement) {
-  return { type: DROPDOWN_MENU_OPEN, id, placement };
+export function openDropdownMenu(id, placement, keyboard) {
+  return { type: DROPDOWN_MENU_OPEN, id, placement, keyboard };
 }
 
 export function closeDropdownMenu(id) {
diff --git a/app/javascript/mastodon/components/__tests__/__snapshots__/button-test.js.snap b/app/javascript/mastodon/components/__tests__/__snapshots__/button-test.js.snap
index c3f018d90..5c04e0979 100644
--- a/app/javascript/mastodon/components/__tests__/__snapshots__/button-test.js.snap
+++ b/app/javascript/mastodon/components/__tests__/__snapshots__/button-test.js.snap
@@ -3,7 +3,6 @@
 exports[`<Button /> adds class "button-secondary" if props.secondary given 1`] = `
 <button
   className="button button-secondary"
-  disabled={undefined}
   onClick={[Function]}
   style={
     Object {
@@ -18,7 +17,6 @@ exports[`<Button /> adds class "button-secondary" if props.secondary given 1`] =
 exports[`<Button /> renders a button element 1`] = `
 <button
   className="button"
-  disabled={undefined}
   onClick={[Function]}
   style={
     Object {
@@ -48,7 +46,6 @@ exports[`<Button /> renders a disabled attribute if props.disabled given 1`] = `
 exports[`<Button /> renders class="button--block" if props.block given 1`] = `
 <button
   className="button button--block"
-  disabled={undefined}
   onClick={[Function]}
   style={
     Object {
@@ -63,7 +60,6 @@ exports[`<Button /> renders class="button--block" if props.block given 1`] = `
 exports[`<Button /> renders the children 1`] = `
 <button
   className="button"
-  disabled={undefined}
   onClick={[Function]}
   style={
     Object {
@@ -82,7 +78,6 @@ exports[`<Button /> renders the children 1`] = `
 exports[`<Button /> renders the given text 1`] = `
 <button
   className="button"
-  disabled={undefined}
   onClick={[Function]}
   style={
     Object {
@@ -99,7 +94,6 @@ exports[`<Button /> renders the given text 1`] = `
 exports[`<Button /> renders the props.text instead of children 1`] = `
 <button
   className="button"
-  disabled={undefined}
   onClick={[Function]}
   style={
     Object {
diff --git a/app/javascript/mastodon/components/account.js b/app/javascript/mastodon/components/account.js
index 8e6aa9d92..c021e3267 100644
--- a/app/javascript/mastodon/components/account.js
+++ b/app/javascript/mastodon/components/account.js
@@ -19,8 +19,8 @@ const messages = defineMessages({
   unmute_notifications: { id: 'account.unmute_notifications', defaultMessage: 'Unmute notifications from @{name}' },
 });
 
-@injectIntl
-export default class Account extends ImmutablePureComponent {
+export default @injectIntl
+class Account extends ImmutablePureComponent {
 
   static propTypes = {
     account: ImmutablePropTypes.map.isRequired,
diff --git a/app/javascript/mastodon/components/column_header.js b/app/javascript/mastodon/components/column_header.js
index 56453aeac..457508d13 100644
--- a/app/javascript/mastodon/components/column_header.js
+++ b/app/javascript/mastodon/components/column_header.js
@@ -10,8 +10,8 @@ const messages = defineMessages({
   moveRight: { id: 'column_header.moveRight_settings', defaultMessage: 'Move column to the right' },
 });
 
-@injectIntl
-export default class ColumnHeader extends React.PureComponent {
+export default @injectIntl
+class ColumnHeader extends React.PureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
diff --git a/app/javascript/mastodon/components/domain.js b/app/javascript/mastodon/components/domain.js
index f657cb8d2..24f80e788 100644
--- a/app/javascript/mastodon/components/domain.js
+++ b/app/javascript/mastodon/components/domain.js
@@ -8,8 +8,8 @@ const messages = defineMessages({
   unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
 });
 
-@injectIntl
-export default class Account extends ImmutablePureComponent {
+export default @injectIntl
+class Account extends ImmutablePureComponent {
 
   static propTypes = {
     domain: PropTypes.string,
diff --git a/app/javascript/mastodon/components/dropdown_menu.js b/app/javascript/mastodon/components/dropdown_menu.js
index a5cf6479b..91b65a02f 100644
--- a/app/javascript/mastodon/components/dropdown_menu.js
+++ b/app/javascript/mastodon/components/dropdown_menu.js
@@ -23,6 +23,7 @@ class DropdownMenu extends React.PureComponent {
     placement: PropTypes.string,
     arrowOffsetLeft: PropTypes.string,
     arrowOffsetTop: PropTypes.string,
+    openedViaKeyboard: PropTypes.bool,
   };
 
   static defaultProps = {
@@ -42,13 +43,15 @@ class DropdownMenu extends React.PureComponent {
 
   componentDidMount () {
     document.addEventListener('click', this.handleDocumentClick, false);
+    document.addEventListener('keydown', this.handleKeyDown, false);
     document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
-    if (this.focusedItem) this.focusedItem.focus();
+    if (this.focusedItem && this.props.openedViaKeyboard) this.focusedItem.focus();
     this.setState({ mounted: true });
   }
 
   componentWillUnmount () {
     document.removeEventListener('click', this.handleDocumentClick, false);
+    document.removeEventListener('keydown', this.handleKeyDown, false);
     document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
   }
 
@@ -62,13 +65,10 @@ class DropdownMenu extends React.PureComponent {
 
   handleKeyDown = e => {
     const items = Array.from(this.node.getElementsByTagName('a'));
-    const index = items.indexOf(e.currentTarget);
+    const index = items.indexOf(document.activeElement);
     let element;
 
     switch(e.key) {
-    case 'Enter':
-      this.handleClick(e);
-      break;
     case 'ArrowDown':
       element = items[index+1];
       if (element) {
@@ -96,6 +96,12 @@ class DropdownMenu extends React.PureComponent {
     }
   }
 
+  handleItemKeyDown = e => {
+    if (e.key === 'Enter') {
+      this.handleClick(e);
+    }
+  }
+
   handleClick = e => {
     const i = Number(e.currentTarget.getAttribute('data-index'));
     const { action, to } = this.props.items[i];
@@ -120,7 +126,7 @@ class DropdownMenu extends React.PureComponent {
 
     return (
       <li className='dropdown-menu__item' key={`${text}-${i}`}>
-        <a href={href} target='_blank' rel='noopener' role='button' tabIndex='0' ref={i === 0 ? this.setFocusRef : null} onClick={this.handleClick} onKeyDown={this.handleKeyDown} data-index={i}>
+        <a href={href} target='_blank' rel='noopener' role='button' tabIndex='0' ref={i === 0 ? this.setFocusRef : null} onClick={this.handleClick} onKeyDown={this.handleItemKeyDown} data-index={i}>
           {text}
         </a>
       </li>
@@ -170,6 +176,7 @@ export default class Dropdown extends React.PureComponent {
     onClose: PropTypes.func.isRequired,
     dropdownPlacement: PropTypes.string,
     openDropdownId: PropTypes.number,
+    openedViaKeyboard: PropTypes.bool,
   };
 
   static defaultProps = {
@@ -180,14 +187,14 @@ export default class Dropdown extends React.PureComponent {
     id: id++,
   };
 
-  handleClick = ({ target }) => {
+  handleClick = ({ target, type }) => {
     if (this.state.id === this.props.openDropdownId) {
       this.handleClose();
     } else {
       const { top } = target.getBoundingClientRect();
       const placement = top * 2 < innerHeight ? 'bottom' : 'top';
 
-      this.props.onOpen(this.state.id, this.handleItemClick, placement);
+      this.props.onOpen(this.state.id, this.handleItemClick, placement, type !== 'click');
     }
   }
 
@@ -197,6 +204,11 @@ export default class Dropdown extends React.PureComponent {
 
   handleKeyDown = e => {
     switch(e.key) {
+    case ' ':
+    case 'Enter':
+      this.handleClick(e);
+      e.preventDefault();
+      break;
     case 'Escape':
       this.handleClose();
       break;
@@ -233,7 +245,7 @@ export default class Dropdown extends React.PureComponent {
   }
 
   render () {
-    const { icon, items, size, title, disabled, dropdownPlacement, openDropdownId } = this.props;
+    const { icon, items, size, title, disabled, dropdownPlacement, openDropdownId, openedViaKeyboard } = this.props;
     const open = this.state.id === openDropdownId;
 
     return (
@@ -249,7 +261,7 @@ export default class Dropdown extends React.PureComponent {
         />
 
         <Overlay show={open} placement={dropdownPlacement} target={this.findTarget}>
-          <DropdownMenu items={items} onClose={this.handleClose} />
+          <DropdownMenu items={items} onClose={this.handleClose} openedViaKeyboard={openedViaKeyboard} />
         </Overlay>
       </div>
     );
diff --git a/app/javascript/mastodon/components/load_gap.js b/app/javascript/mastodon/components/load_gap.js
index 012303ae1..ed4d445d0 100644
--- a/app/javascript/mastodon/components/load_gap.js
+++ b/app/javascript/mastodon/components/load_gap.js
@@ -6,8 +6,8 @@ const messages = defineMessages({
   load_more: { id: 'status.load_more', defaultMessage: 'Load more' },
 });
 
-@injectIntl
-export default class LoadGap extends React.PureComponent {
+export default @injectIntl
+class LoadGap extends React.PureComponent {
 
   static propTypes = {
     disabled: PropTypes.bool,
diff --git a/app/javascript/mastodon/components/media_gallery.js b/app/javascript/mastodon/components/media_gallery.js
index 6e1310cd6..a1785196f 100644
--- a/app/javascript/mastodon/components/media_gallery.js
+++ b/app/javascript/mastodon/components/media_gallery.js
@@ -179,8 +179,8 @@ class Item extends React.PureComponent {
 
 }
 
-@injectIntl
-export default class MediaGallery extends React.PureComponent {
+export default @injectIntl
+class MediaGallery extends React.PureComponent {
 
   static propTypes = {
     sensitive: PropTypes.bool,
diff --git a/app/javascript/mastodon/components/relative_timestamp.js b/app/javascript/mastodon/components/relative_timestamp.js
index 9609714a1..57d99dd19 100644
--- a/app/javascript/mastodon/components/relative_timestamp.js
+++ b/app/javascript/mastodon/components/relative_timestamp.js
@@ -86,8 +86,8 @@ export const timeAgoString = (intl, date, now, year) => {
   return relativeTime;
 };
 
-@injectIntl
-export default class RelativeTimestamp extends React.Component {
+export default @injectIntl
+class RelativeTimestamp extends React.Component {
 
   static propTypes = {
     intl: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js
index 43647acc0..6c595c712 100644
--- a/app/javascript/mastodon/components/status.js
+++ b/app/javascript/mastodon/components/status.js
@@ -35,8 +35,8 @@ export const textForScreenReader = (intl, status, rebloggedByText = false) => {
   return values.join(', ');
 };
 
-@injectIntl
-export default class Status extends ImmutablePureComponent {
+export default @injectIntl
+class Status extends ImmutablePureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.js
index 6d44a4b45..e7e5b0a6c 100644
--- a/app/javascript/mastodon/components/status_action_bar.js
+++ b/app/javascript/mastodon/components/status_action_bar.js
@@ -42,8 +42,8 @@ const obfuscatedCount = count => {
   }
 };
 
-@injectIntl
-export default class StatusActionBar extends ImmutablePureComponent {
+export default @injectIntl
+class StatusActionBar extends ImmutablePureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
diff --git a/app/javascript/mastodon/containers/dropdown_menu_container.js b/app/javascript/mastodon/containers/dropdown_menu_container.js
index 7cbcdcd35..73c8a1e53 100644
--- a/app/javascript/mastodon/containers/dropdown_menu_container.js
+++ b/app/javascript/mastodon/containers/dropdown_menu_container.js
@@ -8,15 +8,16 @@ const mapStateToProps = state => ({
   isModalOpen: state.get('modal').modalType === 'ACTIONS',
   dropdownPlacement: state.getIn(['dropdown_menu', 'placement']),
   openDropdownId: state.getIn(['dropdown_menu', 'openId']),
+  openedViaKeyboard: state.getIn(['dropdown_menu', 'keyboard']),
 });
 
 const mapDispatchToProps = (dispatch, { status, items }) => ({
-  onOpen(id, onItemClick, dropdownPlacement) {
+  onOpen(id, onItemClick, dropdownPlacement, keyboard) {
     dispatch(isUserTouching() ? openModal('ACTIONS', {
       status,
       actions: items,
       onClick: onItemClick,
-    }) : openDropdownMenu(id, dropdownPlacement));
+    }) : openDropdownMenu(id, dropdownPlacement, keyboard));
   },
   onClose(id) {
     dispatch(closeModal());
diff --git a/app/javascript/mastodon/features/account/components/action_bar.js b/app/javascript/mastodon/features/account/components/action_bar.js
index bc6f86628..c9ae2df98 100644
--- a/app/javascript/mastodon/features/account/components/action_bar.js
+++ b/app/javascript/mastodon/features/account/components/action_bar.js
@@ -2,7 +2,7 @@ import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
-import { Link } from 'react-router-dom';
+import { NavLink } from 'react-router-dom';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import { me } from '../../../initial_state';
 import { shortNumberFormat } from '../../../utils/numbers';
@@ -36,8 +36,8 @@ const messages = defineMessages({
   unendorse: { id: 'account.unendorse', defaultMessage: 'Don\'t feature on profile' },
 });
 
-@injectIntl
-export default class ActionBar extends React.PureComponent {
+export default @injectIntl
+class ActionBar extends React.PureComponent {
 
   static propTypes = {
     account: ImmutablePropTypes.map.isRequired,
@@ -60,6 +60,13 @@ export default class ActionBar extends React.PureComponent {
     });
   }
 
+  isStatusesPageActive = (match, location) => {
+    if (!match) {
+      return false;
+    }
+    return !location.pathname.match(/\/(followers|following)\/?$/);
+  }
+
   render () {
     const { account, intl } = this.props;
 
@@ -147,20 +154,20 @@ export default class ActionBar extends React.PureComponent {
 
         <div className='account__action-bar'>
           <div className='account__action-bar-links'>
-            <Link className='account__action-bar__tab' to={`/accounts/${account.get('id')}`} title={intl.formatNumber(account.get('statuses_count'))}>
+            <NavLink isActive={this.isStatusesPageActive} activeClassName='active' className='account__action-bar__tab' to={`/accounts/${account.get('id')}`} title={intl.formatNumber(account.get('statuses_count'))}>
               <FormattedMessage id='account.posts' defaultMessage='Toots' />
               <strong>{shortNumberFormat(account.get('statuses_count'))}</strong>
-            </Link>
+            </NavLink>
 
-            <Link className='account__action-bar__tab' to={`/accounts/${account.get('id')}/following`} title={intl.formatNumber(account.get('following_count'))}>
+            <NavLink exact activeClassName='active' className='account__action-bar__tab' to={`/accounts/${account.get('id')}/following`} title={intl.formatNumber(account.get('following_count'))}>
               <FormattedMessage id='account.follows' defaultMessage='Follows' />
               <strong>{shortNumberFormat(account.get('following_count'))}</strong>
-            </Link>
+            </NavLink>
 
-            <Link className='account__action-bar__tab' to={`/accounts/${account.get('id')}/followers`} title={intl.formatNumber(account.get('followers_count'))}>
+            <NavLink exact activeClassName='active' className='account__action-bar__tab' to={`/accounts/${account.get('id')}/followers`} title={intl.formatNumber(account.get('followers_count'))}>
               <FormattedMessage id='account.followers' defaultMessage='Followers' />
               <strong>{shortNumberFormat(account.get('followers_count'))}</strong>
-            </Link>
+            </NavLink>
           </div>
 
           <div className='account__action-bar-dropdown'>
diff --git a/app/javascript/mastodon/features/account/components/header.js b/app/javascript/mastodon/features/account/components/header.js
index eb9236aeb..11ae58905 100644
--- a/app/javascript/mastodon/features/account/components/header.js
+++ b/app/javascript/mastodon/features/account/components/header.js
@@ -15,8 +15,18 @@ const messages = defineMessages({
   requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' },
   unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
   edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
+  linkVerifiedOn: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' },
 });
 
+const dateFormatOptions = {
+  month: 'short',
+  day: 'numeric',
+  year: 'numeric',
+  hour12: false,
+  hour: '2-digit',
+  minute: '2-digit',
+};
+
 class Avatar extends ImmutablePureComponent {
 
   static propTypes = {
@@ -65,8 +75,8 @@ class Avatar extends ImmutablePureComponent {
 
 }
 
-@injectIntl
-export default class Header extends ImmutablePureComponent {
+export default @injectIntl
+class Header extends ImmutablePureComponent {
 
   static propTypes = {
     account: ImmutablePropTypes.map,
@@ -163,7 +173,10 @@ export default class Header extends ImmutablePureComponent {
               {fields.map((pair, i) => (
                 <dl key={i}>
                   <dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} />
-                  <dd dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} title={pair.get('value_plain')} />
+
+                  <dd className={pair.get('verified_at') && 'verified'} title={pair.get('value_plain')}>
+                    {pair.get('verified_at') && <span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(pair.get('verified_at'), dateFormatOptions) })}><i className='fa fa-check verified__mark' /></span>} <span dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} />
+                  </dd>
                 </dl>
               ))}
             </div>
diff --git a/app/javascript/mastodon/features/account_gallery/index.js b/app/javascript/mastodon/features/account_gallery/index.js
index a6c464aff..32cb5ebdc 100644
--- a/app/javascript/mastodon/features/account_gallery/index.js
+++ b/app/javascript/mastodon/features/account_gallery/index.js
@@ -43,8 +43,8 @@ class LoadMoreMedia extends ImmutablePureComponent {
 
 }
 
-@connect(mapStateToProps)
-export default class AccountGallery extends ImmutablePureComponent {
+export default @connect(mapStateToProps)
+class AccountGallery extends ImmutablePureComponent {
 
   static propTypes = {
     params: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/account_timeline/index.js b/app/javascript/mastodon/features/account_timeline/index.js
index 934513cd7..6055af51d 100644
--- a/app/javascript/mastodon/features/account_timeline/index.js
+++ b/app/javascript/mastodon/features/account_timeline/index.js
@@ -23,8 +23,8 @@ const mapStateToProps = (state, { params: { accountId }, withReplies = false })
   };
 };
 
-@connect(mapStateToProps)
-export default class AccountTimeline extends ImmutablePureComponent {
+export default @connect(mapStateToProps)
+class AccountTimeline extends ImmutablePureComponent {
 
   static propTypes = {
     params: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/blocks/index.js b/app/javascript/mastodon/features/blocks/index.js
index 68661a37c..ca7ce6f8e 100644
--- a/app/javascript/mastodon/features/blocks/index.js
+++ b/app/javascript/mastodon/features/blocks/index.js
@@ -20,9 +20,9 @@ const mapStateToProps = state => ({
   accountIds: state.getIn(['user_lists', 'blocks', 'items']),
 });
 
-@connect(mapStateToProps)
+export default @connect(mapStateToProps)
 @injectIntl
-export default class Blocks extends ImmutablePureComponent {
+class Blocks extends ImmutablePureComponent {
 
   static propTypes = {
     params: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/community_timeline/components/column_settings.js b/app/javascript/mastodon/features/community_timeline/components/column_settings.js
index f4325f58d..8250190a7 100644
--- a/app/javascript/mastodon/features/community_timeline/components/column_settings.js
+++ b/app/javascript/mastodon/features/community_timeline/components/column_settings.js
@@ -4,8 +4,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 import { injectIntl, FormattedMessage } from 'react-intl';
 import SettingToggle from '../../notifications/components/setting_toggle';
 
-@injectIntl
-export default class ColumnSettings extends React.PureComponent {
+export default @injectIntl
+class ColumnSettings extends React.PureComponent {
 
   static propTypes = {
     settings: ImmutablePropTypes.map.isRequired,
diff --git a/app/javascript/mastodon/features/community_timeline/index.js b/app/javascript/mastodon/features/community_timeline/index.js
index 48d2b3f68..7d26c98b0 100644
--- a/app/javascript/mastodon/features/community_timeline/index.js
+++ b/app/javascript/mastodon/features/community_timeline/index.js
@@ -25,9 +25,9 @@ const mapStateToProps = (state, { onlyMedia, columnId }) => {
   };
 };
 
-@connect(mapStateToProps)
+export default @connect(mapStateToProps)
 @injectIntl
-export default class CommunityTimeline extends React.PureComponent {
+class CommunityTimeline extends React.PureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
diff --git a/app/javascript/mastodon/features/compose/components/action_bar.js b/app/javascript/mastodon/features/compose/components/action_bar.js
index daee18552..95d6eeb06 100644
--- a/app/javascript/mastodon/features/compose/components/action_bar.js
+++ b/app/javascript/mastodon/features/compose/components/action_bar.js
@@ -17,8 +17,8 @@ const messages = defineMessages({
   filters: { id: 'navigation_bar.filters', defaultMessage: 'Muted words' },
 });
 
-@injectIntl
-export default class ActionBar extends React.PureComponent {
+export default @injectIntl
+class ActionBar extends React.PureComponent {
 
   static propTypes = {
     account: ImmutablePropTypes.map.isRequired,
diff --git a/app/javascript/mastodon/features/compose/components/compose_form.js b/app/javascript/mastodon/features/compose/components/compose_form.js
index cc9145683..eec93fc28 100644
--- a/app/javascript/mastodon/features/compose/components/compose_form.js
+++ b/app/javascript/mastodon/features/compose/components/compose_form.js
@@ -28,8 +28,8 @@ const messages = defineMessages({
   publishLoud: { id: 'compose_form.publish_loud', defaultMessage: '{publish}!' },
 });
 
-@injectIntl
-export default class ComposeForm extends ImmutablePureComponent {
+export default @injectIntl
+class ComposeForm extends ImmutablePureComponent {
 
   static propTypes = {
     intl: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js
index 4fc32db8a..c1429c756 100644
--- a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js
+++ b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js
@@ -261,6 +261,7 @@ class EmojiPickerMenu extends React.PureComponent {
           skin={skinTone}
           showPreview={false}
           backgroundImageFn={backgroundImageFn}
+          autoFocus
           emojiTooltip
         />
 
@@ -277,8 +278,8 @@ class EmojiPickerMenu extends React.PureComponent {
 
 }
 
-@injectIntl
-export default class EmojiPickerDropdown extends React.PureComponent {
+export default @injectIntl
+class EmojiPickerDropdown extends React.PureComponent {
 
   static propTypes = {
     custom_emojis: ImmutablePropTypes.list,
diff --git a/app/javascript/mastodon/features/compose/components/privacy_dropdown.js b/app/javascript/mastodon/features/compose/components/privacy_dropdown.js
index e19778fd2..7b5482f05 100644
--- a/app/javascript/mastodon/features/compose/components/privacy_dropdown.js
+++ b/app/javascript/mastodon/features/compose/components/privacy_dropdown.js
@@ -149,8 +149,8 @@ class PrivacyDropdownMenu extends React.PureComponent {
 
 }
 
-@injectIntl
-export default class PrivacyDropdown extends React.PureComponent {
+export default @injectIntl
+class PrivacyDropdown extends React.PureComponent {
 
   static propTypes = {
     isUserTouching: PropTypes.func,
diff --git a/app/javascript/mastodon/features/compose/components/reply_indicator.js b/app/javascript/mastodon/features/compose/components/reply_indicator.js
index 6f358a98b..142223f3d 100644
--- a/app/javascript/mastodon/features/compose/components/reply_indicator.js
+++ b/app/javascript/mastodon/features/compose/components/reply_indicator.js
@@ -12,8 +12,8 @@ const messages = defineMessages({
   cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' },
 });
 
-@injectIntl
-export default class ReplyIndicator extends ImmutablePureComponent {
+export default @injectIntl
+class ReplyIndicator extends ImmutablePureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
diff --git a/app/javascript/mastodon/features/compose/components/search.js b/app/javascript/mastodon/features/compose/components/search.js
index 71c0a203f..d203d8780 100644
--- a/app/javascript/mastodon/features/compose/components/search.js
+++ b/app/javascript/mastodon/features/compose/components/search.js
@@ -43,8 +43,8 @@ class SearchPopout extends React.PureComponent {
 
 }
 
-@injectIntl
-export default class Search extends React.PureComponent {
+export default @injectIntl
+class Search extends React.PureComponent {
 
   static propTypes = {
     value: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/features/compose/components/upload.js b/app/javascript/mastodon/features/compose/components/upload.js
index 3d09217dc..e377da90c 100644
--- a/app/javascript/mastodon/features/compose/components/upload.js
+++ b/app/javascript/mastodon/features/compose/components/upload.js
@@ -11,8 +11,8 @@ const messages = defineMessages({
   description: { id: 'upload_form.description', defaultMessage: 'Describe for the visually impaired' },
 });
 
-@injectIntl
-export default class Upload extends ImmutablePureComponent {
+export default @injectIntl
+class Upload extends ImmutablePureComponent {
 
   static propTypes = {
     media: ImmutablePropTypes.map.isRequired,
diff --git a/app/javascript/mastodon/features/compose/components/upload_button.js b/app/javascript/mastodon/features/compose/components/upload_button.js
index 8b9609138..b6fe770ea 100644
--- a/app/javascript/mastodon/features/compose/components/upload_button.js
+++ b/app/javascript/mastodon/features/compose/components/upload_button.js
@@ -23,9 +23,9 @@ const iconStyle = {
   lineHeight: '27px',
 };
 
-@connect(makeMapStateToProps)
+export default @connect(makeMapStateToProps)
 @injectIntl
-export default class UploadButton extends ImmutablePureComponent {
+class UploadButton extends ImmutablePureComponent {
 
   static propTypes = {
     disabled: PropTypes.bool,
diff --git a/app/javascript/mastodon/features/compose/index.js b/app/javascript/mastodon/features/compose/index.js
index b7394a39e..cf1714b71 100644
--- a/app/javascript/mastodon/features/compose/index.js
+++ b/app/javascript/mastodon/features/compose/index.js
@@ -30,9 +30,9 @@ const mapStateToProps = (state, ownProps) => ({
   showSearch: ownProps.multiColumn ? state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']) : ownProps.isSearchPage,
 });
 
-@connect(mapStateToProps)
+export default @connect(mapStateToProps)
 @injectIntl
-export default class Compose extends React.PureComponent {
+class Compose extends React.PureComponent {
 
   static propTypes = {
     dispatch: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/compose/util/url_regex.js b/app/javascript/mastodon/features/compose/util/url_regex.js
index d5e39e0d5..7f1e17620 100644
--- a/app/javascript/mastodon/features/compose/util/url_regex.js
+++ b/app/javascript/mastodon/features/compose/util/url_regex.js
@@ -170,7 +170,7 @@ export const urlRegex = (function() {
       ')'                                   +
     '\\)',
     'i');
-  // Valid end-of-path chracters (so /foo. does not gobble the period).
+  // Valid end-of-path characters (so /foo. does not gobble the period).
   // 1. Allow =&# for empty URL parameters and other URL-join artifacts
   regexen.validUrlPathEndingChars = regexSupplant(/[^#{spaces_group}\(\)\?!\*';:=\,\.\$%\[\]#{pd}~&\|@]|(?:#{validUrlBalancedParens})/i);
   // Allow @ in a url, but only in the middle. Catch things like http://example.com/@user/
diff --git a/app/javascript/mastodon/features/direct_timeline/components/column_settings.js b/app/javascript/mastodon/features/direct_timeline/components/column_settings.js
index a992b27bb..b629c128d 100644
--- a/app/javascript/mastodon/features/direct_timeline/components/column_settings.js
+++ b/app/javascript/mastodon/features/direct_timeline/components/column_settings.js
@@ -9,8 +9,8 @@ const messages = defineMessages({
   settings: { id: 'home.settings', defaultMessage: 'Column settings' },
 });
 
-@injectIntl
-export default class ColumnSettings extends React.PureComponent {
+export default @injectIntl
+class ColumnSettings extends React.PureComponent {
 
   static propTypes = {
     settings: ImmutablePropTypes.map.isRequired,
diff --git a/app/javascript/mastodon/features/direct_timeline/index.js b/app/javascript/mastodon/features/direct_timeline/index.js
index dd289ce56..3c7e2d007 100644
--- a/app/javascript/mastodon/features/direct_timeline/index.js
+++ b/app/javascript/mastodon/features/direct_timeline/index.js
@@ -17,9 +17,9 @@ const mapStateToProps = state => ({
   hasUnread: state.getIn(['timelines', 'direct', 'unread']) > 0,
 });
 
-@connect(mapStateToProps)
+export default @connect(mapStateToProps)
 @injectIntl
-export default class DirectTimeline extends React.PureComponent {
+class DirectTimeline extends React.PureComponent {
 
   static propTypes = {
     dispatch: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/domain_blocks/index.js b/app/javascript/mastodon/features/domain_blocks/index.js
index 2c40bf72d..5c1bd1161 100644
--- a/app/javascript/mastodon/features/domain_blocks/index.js
+++ b/app/javascript/mastodon/features/domain_blocks/index.js
@@ -21,9 +21,9 @@ const mapStateToProps = state => ({
   domains: state.getIn(['domain_lists', 'blocks', 'items']),
 });
 
-@connect(mapStateToProps)
+export default @connect(mapStateToProps)
 @injectIntl
-export default class Blocks extends ImmutablePureComponent {
+class Blocks extends ImmutablePureComponent {
 
   static propTypes = {
     params: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js b/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js
index d91b48497..c8425c4c6 100644
--- a/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js
+++ b/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js
@@ -73,5 +73,10 @@ describe('emoji', () => {
       expect(emojify('<span class="invisible">😄<br/>😴</span>😇'))
         .toEqual('<span class="invisible">😄<br/>😴</span><img draggable="false" class="emojione" alt="😇" title=":innocent:" src="/emoji/1f607.svg" />');
     });
+
+    it('skips the textual presentation VS15 character', () => {
+      expect(emojify('✴︎')) // This is U+2734 EIGHT POINTED BLACK STAR then U+FE0E VARIATION SELECTOR-15
+        .toEqual('<img draggable="false" class="emojione" alt="✴" title=":eight_pointed_black_star:" src="/emoji/2734.svg" />');
+    });
   });
 });
diff --git a/app/javascript/mastodon/features/emoji/emoji.js b/app/javascript/mastodon/features/emoji/emoji.js
index 0f005dd50..988cea253 100644
--- a/app/javascript/mastodon/features/emoji/emoji.js
+++ b/app/javascript/mastodon/features/emoji/emoji.js
@@ -62,6 +62,10 @@ const emojify = (str, customEmojis = {}) => {
       const title = shortCode ? `:${shortCode}:` : '';
       replacement = `<img draggable="false" class="emojione" alt="${match}" title="${title}" src="${assetHost}/emoji/${filename}.svg" />`;
       rend = i + match.length;
+      // If the matched character was followed by VS15 (for selecting text presentation), skip it.
+      if (str.codePointAt(rend) === 65038) {
+        rend += 1;
+      }
     }
     rtn += str.slice(0, i) + replacement;
     str = str.slice(rend);
diff --git a/app/javascript/mastodon/features/favourited_statuses/index.js b/app/javascript/mastodon/features/favourited_statuses/index.js
index 499530cd9..fa9401b90 100644
--- a/app/javascript/mastodon/features/favourited_statuses/index.js
+++ b/app/javascript/mastodon/features/favourited_statuses/index.js
@@ -21,9 +21,9 @@ const mapStateToProps = state => ({
   hasMore: !!state.getIn(['status_lists', 'favourites', 'next']),
 });
 
-@connect(mapStateToProps)
+export default @connect(mapStateToProps)
 @injectIntl
-export default class Favourites extends ImmutablePureComponent {
+class Favourites extends ImmutablePureComponent {
 
   static propTypes = {
     dispatch: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/favourites/index.js b/app/javascript/mastodon/features/favourites/index.js
index 74a683ccc..d1ac229a2 100644
--- a/app/javascript/mastodon/features/favourites/index.js
+++ b/app/javascript/mastodon/features/favourites/index.js
@@ -15,8 +15,8 @@ const mapStateToProps = (state, props) => ({
   accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId]),
 });
 
-@connect(mapStateToProps)
-export default class Favourites extends ImmutablePureComponent {
+export default @connect(mapStateToProps)
+class Favourites extends ImmutablePureComponent {
 
   static propTypes = {
     params: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/follow_requests/components/account_authorize.js b/app/javascript/mastodon/features/follow_requests/components/account_authorize.js
index 4fc5638d9..a3b524db1 100644
--- a/app/javascript/mastodon/features/follow_requests/components/account_authorize.js
+++ b/app/javascript/mastodon/features/follow_requests/components/account_authorize.js
@@ -13,8 +13,8 @@ const messages = defineMessages({
   reject: { id: 'follow_request.reject', defaultMessage: 'Reject' },
 });
 
-@injectIntl
-export default class AccountAuthorize extends ImmutablePureComponent {
+export default @injectIntl
+class AccountAuthorize extends ImmutablePureComponent {
 
   static propTypes = {
     account: ImmutablePropTypes.map.isRequired,
diff --git a/app/javascript/mastodon/features/follow_requests/index.js b/app/javascript/mastodon/features/follow_requests/index.js
index cb574e08d..56ae8764b 100644
--- a/app/javascript/mastodon/features/follow_requests/index.js
+++ b/app/javascript/mastodon/features/follow_requests/index.js
@@ -20,9 +20,9 @@ const mapStateToProps = state => ({
   accountIds: state.getIn(['user_lists', 'follow_requests', 'items']),
 });
 
-@connect(mapStateToProps)
+export default @connect(mapStateToProps)
 @injectIntl
-export default class FollowRequests extends ImmutablePureComponent {
+class FollowRequests extends ImmutablePureComponent {
 
   static propTypes = {
     params: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/followers/index.js b/app/javascript/mastodon/features/followers/index.js
index 97d59cc4a..b9ca7f3dd 100644
--- a/app/javascript/mastodon/features/followers/index.js
+++ b/app/javascript/mastodon/features/followers/index.js
@@ -22,8 +22,8 @@ const mapStateToProps = (state, props) => ({
   hasMore: !!state.getIn(['user_lists', 'followers', props.params.accountId, 'next']),
 });
 
-@connect(mapStateToProps)
-export default class Followers extends ImmutablePureComponent {
+export default @connect(mapStateToProps)
+class Followers extends ImmutablePureComponent {
 
   static propTypes = {
     params: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/following/index.js b/app/javascript/mastodon/features/following/index.js
index 17bc7ec6e..b3e160240 100644
--- a/app/javascript/mastodon/features/following/index.js
+++ b/app/javascript/mastodon/features/following/index.js
@@ -22,8 +22,8 @@ const mapStateToProps = (state, props) => ({
   hasMore: !!state.getIn(['user_lists', 'following', props.params.accountId, 'next']),
 });
 
-@connect(mapStateToProps)
-export default class Following extends ImmutablePureComponent {
+export default @connect(mapStateToProps)
+class Following extends ImmutablePureComponent {
 
   static propTypes = {
     params: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/getting_started/index.js b/app/javascript/mastodon/features/getting_started/index.js
index f34ac6b8a..d5ba5cbce 100644
--- a/app/javascript/mastodon/features/getting_started/index.js
+++ b/app/javascript/mastodon/features/getting_started/index.js
@@ -53,9 +53,9 @@ const badgeDisplay = (number, limit) => {
   }
 };
 
-@connect(mapStateToProps, mapDispatchToProps)
+export default @connect(mapStateToProps, mapDispatchToProps)
 @injectIntl
-export default class GettingStarted extends ImmutablePureComponent {
+class GettingStarted extends ImmutablePureComponent {
 
   static propTypes = {
     intl: PropTypes.object.isRequired,
@@ -126,34 +126,36 @@ export default class GettingStarted extends ImmutablePureComponent {
           </h1>
         </div>}
 
-        <div className='getting-started__wrapper' style={{ height }}>
-          {!multiColumn && <NavigationBar account={myAccount} />}
-          {navItems}
-        </div>
-
-        {!multiColumn && <div className='flex-spacer' />}
-
-        <div className='getting-started getting-started__footer'>
-          <ul>
-            <li><a href='https://bridge.joinmastodon.org/' target='_blank'><FormattedMessage id='getting_started.find_friends' defaultMessage='Find friends from Twitter' /></a> · </li>
-            {invitesEnabled && <li><a href='/invites' target='_blank'><FormattedMessage id='getting_started.invite' defaultMessage='Invite people' /></a> · </li>}
-            {multiColumn && <li><Link to='/keyboard-shortcuts'><FormattedMessage id='navigation_bar.keyboard_shortcuts' defaultMessage='Hotkeys' /></Link> · </li>}
-            <li><a href='/auth/edit'><FormattedMessage id='getting_started.security' defaultMessage='Security' /></a> · </li>
-            <li><a href='/about/more' target='_blank'><FormattedMessage id='navigation_bar.info' defaultMessage='About this instance' /></a> · </li>
-            <li><a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='navigation_bar.apps' defaultMessage='Mobile apps' /></a> · </li>
-            <li><a href='/terms' target='_blank'><FormattedMessage id='getting_started.terms' defaultMessage='Terms of service' /></a> · </li>
-            <li><a href='/settings/applications' target='_blank'><FormattedMessage id='getting_started.developers' defaultMessage='Developers' /></a> · </li>
-            <li><a href='https://github.com/tootsuite/documentation#documentation' target='_blank'><FormattedMessage id='getting_started.documentation' defaultMessage='Documentation' /></a> · </li>
-            <li><a href='/auth/sign_out' data-method='delete'><FormattedMessage id='navigation_bar.logout' defaultMessage='Logout' /></a></li>
-          </ul>
-
-          <p>
-            <FormattedMessage
-              id='getting_started.open_source_notice'
-              defaultMessage='Mastodon is open source software. You can contribute or report issues on GitHub at {github}.'
-              values={{ github: <span><a href='https://github.com/tootsuite/mastodon' rel='noopener' target='_blank'>tootsuite/mastodon</a> (v{version})</span> }}
-            />
-          </p>
+        <div className='getting-started'>
+          <div className='getting-started__wrapper' style={{ height }}>
+            {!multiColumn && <NavigationBar account={myAccount} />}
+            {navItems}
+          </div>
+
+          {!multiColumn && <div className='flex-spacer' />}
+
+          <div className='getting-started__footer'>
+            <ul>
+              <li><a href='https://bridge.joinmastodon.org/' target='_blank'><FormattedMessage id='getting_started.find_friends' defaultMessage='Find friends from Twitter' /></a> · </li>
+              {invitesEnabled && <li><a href='/invites' target='_blank'><FormattedMessage id='getting_started.invite' defaultMessage='Invite people' /></a> · </li>}
+              {multiColumn && <li><Link to='/keyboard-shortcuts'><FormattedMessage id='navigation_bar.keyboard_shortcuts' defaultMessage='Hotkeys' /></Link> · </li>}
+              <li><a href='/auth/edit'><FormattedMessage id='getting_started.security' defaultMessage='Security' /></a> · </li>
+              <li><a href='/about/more' target='_blank'><FormattedMessage id='navigation_bar.info' defaultMessage='About this instance' /></a> · </li>
+              <li><a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='navigation_bar.apps' defaultMessage='Mobile apps' /></a> · </li>
+              <li><a href='/terms' target='_blank'><FormattedMessage id='getting_started.terms' defaultMessage='Terms of service' /></a> · </li>
+              <li><a href='/settings/applications' target='_blank'><FormattedMessage id='getting_started.developers' defaultMessage='Developers' /></a> · </li>
+              <li><a href='https://github.com/tootsuite/documentation#documentation' target='_blank'><FormattedMessage id='getting_started.documentation' defaultMessage='Documentation' /></a> · </li>
+              <li><a href='/auth/sign_out' data-method='delete'><FormattedMessage id='navigation_bar.logout' defaultMessage='Logout' /></a></li>
+            </ul>
+
+            <p>
+              <FormattedMessage
+                id='getting_started.open_source_notice'
+                defaultMessage='Mastodon is open source software. You can contribute or report issues on GitHub at {github}.'
+                values={{ github: <span><a href='https://github.com/tootsuite/mastodon' rel='noopener' target='_blank'>tootsuite/mastodon</a> (v{version})</span> }}
+              />
+            </p>
+          </div>
         </div>
       </Column>
     );
diff --git a/app/javascript/mastodon/features/hashtag_timeline/index.js b/app/javascript/mastodon/features/hashtag_timeline/index.js
index b67486f07..63efdf1bd 100644
--- a/app/javascript/mastodon/features/hashtag_timeline/index.js
+++ b/app/javascript/mastodon/features/hashtag_timeline/index.js
@@ -13,8 +13,8 @@ const mapStateToProps = (state, props) => ({
   hasUnread: state.getIn(['timelines', `hashtag:${props.params.id}`, 'unread']) > 0,
 });
 
-@connect(mapStateToProps)
-export default class HashtagTimeline extends React.PureComponent {
+export default @connect(mapStateToProps)
+class HashtagTimeline extends React.PureComponent {
 
   static propTypes = {
     params: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/home_timeline/components/column_settings.js b/app/javascript/mastodon/features/home_timeline/components/column_settings.js
index 932ac2049..455e21881 100644
--- a/app/javascript/mastodon/features/home_timeline/components/column_settings.js
+++ b/app/javascript/mastodon/features/home_timeline/components/column_settings.js
@@ -4,8 +4,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 import { injectIntl, FormattedMessage } from 'react-intl';
 import SettingToggle from '../../notifications/components/setting_toggle';
 
-@injectIntl
-export default class ColumnSettings extends React.PureComponent {
+export default @injectIntl
+class ColumnSettings extends React.PureComponent {
 
   static propTypes = {
     settings: ImmutablePropTypes.map.isRequired,
diff --git a/app/javascript/mastodon/features/home_timeline/index.js b/app/javascript/mastodon/features/home_timeline/index.js
index 12dab0e44..3ffa7a681 100644
--- a/app/javascript/mastodon/features/home_timeline/index.js
+++ b/app/javascript/mastodon/features/home_timeline/index.js
@@ -19,9 +19,9 @@ const mapStateToProps = state => ({
   isPartial: state.getIn(['timelines', 'home', 'items', 0], null) === null,
 });
 
-@connect(mapStateToProps)
+export default @connect(mapStateToProps)
 @injectIntl
-export default class HomeTimeline extends React.PureComponent {
+class HomeTimeline extends React.PureComponent {
 
   static propTypes = {
     dispatch: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/keyboard_shortcuts/index.js b/app/javascript/mastodon/features/keyboard_shortcuts/index.js
index 2d86953c6..ab1ac511e 100644
--- a/app/javascript/mastodon/features/keyboard_shortcuts/index.js
+++ b/app/javascript/mastodon/features/keyboard_shortcuts/index.js
@@ -9,8 +9,8 @@ const messages = defineMessages({
   heading: { id: 'keyboard_shortcuts.heading', defaultMessage: 'Keyboard Shortcuts' },
 });
 
-@injectIntl
-export default class KeyboardShortcuts extends ImmutablePureComponent {
+export default @injectIntl
+class KeyboardShortcuts extends ImmutablePureComponent {
 
   static propTypes = {
     intl: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/list_editor/components/account.js b/app/javascript/mastodon/features/list_editor/components/account.js
index c78c58e24..48085af43 100644
--- a/app/javascript/mastodon/features/list_editor/components/account.js
+++ b/app/javascript/mastodon/features/list_editor/components/account.js
@@ -31,9 +31,9 @@ const mapDispatchToProps = (dispatch, { accountId }) => ({
   onAdd: () => dispatch(addToListEditor(accountId)),
 });
 
-@connect(makeMapStateToProps, mapDispatchToProps)
+export default @connect(makeMapStateToProps, mapDispatchToProps)
 @injectIntl
-export default class Account extends ImmutablePureComponent {
+class Account extends ImmutablePureComponent {
 
   static propTypes = {
     account: ImmutablePropTypes.map.isRequired,
diff --git a/app/javascript/mastodon/features/list_editor/components/search.js b/app/javascript/mastodon/features/list_editor/components/search.js
index 45c4d0f2e..f7617fe58 100644
--- a/app/javascript/mastodon/features/list_editor/components/search.js
+++ b/app/javascript/mastodon/features/list_editor/components/search.js
@@ -19,9 +19,9 @@ const mapDispatchToProps = dispatch => ({
   onChange: value => dispatch(changeListSuggestions(value)),
 });
 
-@connect(mapStateToProps, mapDispatchToProps)
+export default @connect(mapStateToProps, mapDispatchToProps)
 @injectIntl
-export default class Search extends React.PureComponent {
+class Search extends React.PureComponent {
 
   static propTypes = {
     intl: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/list_editor/index.js b/app/javascript/mastodon/features/list_editor/index.js
index 65f7420de..aab0cdd0c 100644
--- a/app/javascript/mastodon/features/list_editor/index.js
+++ b/app/javascript/mastodon/features/list_editor/index.js
@@ -22,9 +22,9 @@ const mapDispatchToProps = dispatch => ({
   onReset: () => dispatch(resetListEditor()),
 });
 
-@connect(mapStateToProps, mapDispatchToProps)
+export default @connect(mapStateToProps, mapDispatchToProps)
 @injectIntl
-export default class ListEditor extends ImmutablePureComponent {
+class ListEditor extends ImmutablePureComponent {
 
   static propTypes = {
     listId: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/features/list_timeline/index.js b/app/javascript/mastodon/features/list_timeline/index.js
index 164669e89..5b047ace4 100644
--- a/app/javascript/mastodon/features/list_timeline/index.js
+++ b/app/javascript/mastodon/features/list_timeline/index.js
@@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import StatusListContainer from '../ui/containers/status_list_container';
 import Column from '../../components/column';
+import ColumnBackButton from '../../components/column_back_button';
 import ColumnHeader from '../../components/column_header';
 import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
 import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
@@ -24,9 +25,9 @@ const mapStateToProps = (state, props) => ({
   hasUnread: state.getIn(['timelines', `list:${props.params.id}`, 'unread']) > 0,
 });
 
-@connect(mapStateToProps)
+export default @connect(mapStateToProps)
 @injectIntl
-export default class ListTimeline extends React.PureComponent {
+class ListTimeline extends React.PureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
@@ -129,9 +130,8 @@ export default class ListTimeline extends React.PureComponent {
     } else if (list === false) {
       return (
         <Column>
-          <div className='scrollable'>
-            <MissingIndicator />
-          </div>
+          <ColumnBackButton />
+          <MissingIndicator />
         </Column>
       );
     }
diff --git a/app/javascript/mastodon/features/lists/components/new_list_form.js b/app/javascript/mastodon/features/lists/components/new_list_form.js
index eed6efc25..739246640 100644
--- a/app/javascript/mastodon/features/lists/components/new_list_form.js
+++ b/app/javascript/mastodon/features/lists/components/new_list_form.js
@@ -20,9 +20,9 @@ const mapDispatchToProps = dispatch => ({
   onSubmit: () => dispatch(submitListEditor(true)),
 });
 
-@connect(mapStateToProps, mapDispatchToProps)
+export default @connect(mapStateToProps, mapDispatchToProps)
 @injectIntl
-export default class NewListForm extends React.PureComponent {
+class NewListForm extends React.PureComponent {
 
   static propTypes = {
     value: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/features/lists/index.js b/app/javascript/mastodon/features/lists/index.js
index 127347730..24f40c16b 100644
--- a/app/javascript/mastodon/features/lists/index.js
+++ b/app/javascript/mastodon/features/lists/index.js
@@ -31,9 +31,9 @@ const mapStateToProps = state => ({
   lists: getOrderedLists(state),
 });
 
-@connect(mapStateToProps)
+export default @connect(mapStateToProps)
 @injectIntl
-export default class Lists extends ImmutablePureComponent {
+class Lists extends ImmutablePureComponent {
 
   static propTypes = {
     params: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/mutes/index.js b/app/javascript/mastodon/features/mutes/index.js
index 7bf9c1464..f979ef72f 100644
--- a/app/javascript/mastodon/features/mutes/index.js
+++ b/app/javascript/mastodon/features/mutes/index.js
@@ -20,9 +20,9 @@ const mapStateToProps = state => ({
   accountIds: state.getIn(['user_lists', 'mutes', 'items']),
 });
 
-@connect(mapStateToProps)
+export default @connect(mapStateToProps)
 @injectIntl
-export default class Mutes extends ImmutablePureComponent {
+class Mutes extends ImmutablePureComponent {
 
   static propTypes = {
     params: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/notifications/components/notification.js b/app/javascript/mastodon/features/notifications/components/notification.js
index 07fec84b2..ed4a44ca6 100644
--- a/app/javascript/mastodon/features/notifications/components/notification.js
+++ b/app/javascript/mastodon/features/notifications/components/notification.js
@@ -16,8 +16,8 @@ const notificationForScreenReader = (intl, message, timestamp) => {
   return output.join(', ');
 };
 
-@injectIntl
-export default class Notification extends ImmutablePureComponent {
+export default @injectIntl
+class Notification extends ImmutablePureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
diff --git a/app/javascript/mastodon/features/notifications/index.js b/app/javascript/mastodon/features/notifications/index.js
index b7d7f361c..66af0baaf 100644
--- a/app/javascript/mastodon/features/notifications/index.js
+++ b/app/javascript/mastodon/features/notifications/index.js
@@ -31,9 +31,9 @@ const mapStateToProps = state => ({
   hasMore: state.getIn(['notifications', 'hasMore']),
 });
 
-@connect(mapStateToProps)
+export default @connect(mapStateToProps)
 @injectIntl
-export default class Notifications extends React.PureComponent {
+class Notifications extends React.PureComponent {
 
   static propTypes = {
     columnId: PropTypes.string,
diff --git a/app/javascript/mastodon/features/pinned_statuses/index.js b/app/javascript/mastodon/features/pinned_statuses/index.js
index c6eb689d2..98cdbda3c 100644
--- a/app/javascript/mastodon/features/pinned_statuses/index.js
+++ b/app/javascript/mastodon/features/pinned_statuses/index.js
@@ -18,9 +18,9 @@ const mapStateToProps = state => ({
   hasMore: !!state.getIn(['status_lists', 'pins', 'next']),
 });
 
-@connect(mapStateToProps)
+export default @connect(mapStateToProps)
 @injectIntl
-export default class PinnedStatuses extends ImmutablePureComponent {
+class PinnedStatuses extends ImmutablePureComponent {
 
   static propTypes = {
     dispatch: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/public_timeline/index.js b/app/javascript/mastodon/features/public_timeline/index.js
index 6d5c4118d..46d972251 100644
--- a/app/javascript/mastodon/features/public_timeline/index.js
+++ b/app/javascript/mastodon/features/public_timeline/index.js
@@ -25,9 +25,9 @@ const mapStateToProps = (state, { onlyMedia, columnId }) => {
   };
 };
 
-@connect(mapStateToProps)
+export default @connect(mapStateToProps)
 @injectIntl
-export default class PublicTimeline extends React.PureComponent {
+class PublicTimeline extends React.PureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
diff --git a/app/javascript/mastodon/features/reblogs/index.js b/app/javascript/mastodon/features/reblogs/index.js
index acb9b40f9..c05d21c74 100644
--- a/app/javascript/mastodon/features/reblogs/index.js
+++ b/app/javascript/mastodon/features/reblogs/index.js
@@ -15,8 +15,8 @@ const mapStateToProps = (state, props) => ({
   accountIds: state.getIn(['user_lists', 'reblogged_by', props.params.statusId]),
 });
 
-@connect(mapStateToProps)
-export default class Reblogs extends ImmutablePureComponent {
+export default @connect(mapStateToProps)
+class Reblogs extends ImmutablePureComponent {
 
   static propTypes = {
     params: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/standalone/community_timeline/index.js b/app/javascript/mastodon/features/standalone/community_timeline/index.js
index c8ae9b304..f917f41c9 100644
--- a/app/javascript/mastodon/features/standalone/community_timeline/index.js
+++ b/app/javascript/mastodon/features/standalone/community_timeline/index.js
@@ -12,9 +12,9 @@ const messages = defineMessages({
   title: { id: 'standalone.public_title', defaultMessage: 'A look inside...' },
 });
 
-@connect()
+export default @connect()
 @injectIntl
-export default class CommunityTimeline extends React.PureComponent {
+class CommunityTimeline extends React.PureComponent {
 
   static propTypes = {
     dispatch: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js b/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js
index 931ca2a32..45c263257 100644
--- a/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js
+++ b/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js
@@ -7,8 +7,8 @@ import Column from '../../../components/column';
 import ColumnHeader from '../../../components/column_header';
 import { connectHashtagStream } from '../../../actions/streaming';
 
-@connect()
-export default class HashtagTimeline extends React.PureComponent {
+export default @connect()
+class HashtagTimeline extends React.PureComponent {
 
   static propTypes = {
     dispatch: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/standalone/public_timeline/index.js b/app/javascript/mastodon/features/standalone/public_timeline/index.js
index 115c51d85..618696eb1 100644
--- a/app/javascript/mastodon/features/standalone/public_timeline/index.js
+++ b/app/javascript/mastodon/features/standalone/public_timeline/index.js
@@ -12,9 +12,9 @@ const messages = defineMessages({
   title: { id: 'standalone.public_title', defaultMessage: 'A look inside...' },
 });
 
-@connect()
+export default @connect()
 @injectIntl
-export default class PublicTimeline extends React.PureComponent {
+class PublicTimeline extends React.PureComponent {
 
   static propTypes = {
     dispatch: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/status/components/action_bar.js b/app/javascript/mastodon/features/status/components/action_bar.js
index f5977c02c..fa6fd56e5 100644
--- a/app/javascript/mastodon/features/status/components/action_bar.js
+++ b/app/javascript/mastodon/features/status/components/action_bar.js
@@ -28,8 +28,8 @@ const messages = defineMessages({
   embed: { id: 'status.embed', defaultMessage: 'Embed' },
 });
 
-@injectIntl
-export default class ActionBar extends React.PureComponent {
+export default @injectIntl
+class ActionBar extends React.PureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js
index 48931b2d6..7d1bc2ca4 100644
--- a/app/javascript/mastodon/features/status/index.js
+++ b/app/javascript/mastodon/features/status/index.js
@@ -104,9 +104,9 @@ const makeMapStateToProps = () => {
   return mapStateToProps;
 };
 
-@injectIntl
+export default @injectIntl
 @connect(makeMapStateToProps)
-export default class Status extends ImmutablePureComponent {
+class Status extends ImmutablePureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
diff --git a/app/javascript/mastodon/features/ui/components/boost_modal.js b/app/javascript/mastodon/features/ui/components/boost_modal.js
index 1c90d10dd..b128e67d2 100644
--- a/app/javascript/mastodon/features/ui/components/boost_modal.js
+++ b/app/javascript/mastodon/features/ui/components/boost_modal.js
@@ -13,8 +13,8 @@ const messages = defineMessages({
   reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
 });
 
-@injectIntl
-export default class BoostModal extends ImmutablePureComponent {
+export default @injectIntl
+class BoostModal extends ImmutablePureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
diff --git a/app/javascript/mastodon/features/ui/components/columns_area.js b/app/javascript/mastodon/features/ui/components/columns_area.js
index 3ab867b5a..ed338c2eb 100644
--- a/app/javascript/mastodon/features/ui/components/columns_area.js
+++ b/app/javascript/mastodon/features/ui/components/columns_area.js
@@ -1,6 +1,6 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import { injectIntl } from 'react-intl';
+import { defineMessages, injectIntl } from 'react-intl';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 
@@ -29,10 +29,14 @@ const componentMap = {
   'LIST': ListTimeline,
 };
 
+const messages = defineMessages({
+  publish: { id: 'compose_form.publish', defaultMessage: 'Toot' },
+});
+
 const shouldHideFAB = path => path.match(/^\/statuses\//);
 
-@component => injectIntl(component, { withRef: true })
-export default class ColumnsArea extends ImmutablePureComponent {
+export default @(component => injectIntl(component, { withRef: true }))
+class ColumnsArea extends ImmutablePureComponent {
 
   static contextTypes = {
     router: PropTypes.object.isRequired,
@@ -149,14 +153,14 @@ export default class ColumnsArea extends ImmutablePureComponent {
   }
 
   render () {
-    const { columns, children, singleColumn, isModalOpen } = this.props;
+    const { columns, children, singleColumn, isModalOpen, intl } = this.props;
     const { shouldAnimate } = this.state;
 
     const columnIndex = getIndex(this.context.router.history.location.pathname);
     this.pendingIndex = null;
 
     if (singleColumn) {
-      const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : <Link key='floating-action-button' to='/statuses/new' className='floating-action-button'><i className='fa fa-pencil' /></Link>;
+      const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : <Link key='floating-action-button' to='/statuses/new' className='floating-action-button' aria-label={intl.formatMessage(messages.publish)}><i className='fa fa-pencil' /></Link>;
 
       return columnIndex !== -1 ? [
         <ReactSwipeableViews key='content' index={columnIndex} onChangeIndex={this.handleSwipe} onTransitionEnd={this.handleAnimationEnd} animateTransitions={shouldAnimate} springConfig={{ duration: '400ms', delay: '0s', easeFunction: 'ease' }} style={{ height: '100%' }}>
diff --git a/app/javascript/mastodon/features/ui/components/confirmation_modal.js b/app/javascript/mastodon/features/ui/components/confirmation_modal.js
index 86588c46a..f0f3ad134 100644
--- a/app/javascript/mastodon/features/ui/components/confirmation_modal.js
+++ b/app/javascript/mastodon/features/ui/components/confirmation_modal.js
@@ -3,8 +3,8 @@ import PropTypes from 'prop-types';
 import { injectIntl, FormattedMessage } from 'react-intl';
 import Button from '../../../components/button';
 
-@injectIntl
-export default class ConfirmationModal extends React.PureComponent {
+export default @injectIntl
+class ConfirmationModal extends React.PureComponent {
 
   static propTypes = {
     message: PropTypes.node.isRequired,
diff --git a/app/javascript/mastodon/features/ui/components/embed_modal.js b/app/javascript/mastodon/features/ui/components/embed_modal.js
index 52aab00d0..2afb6f3d7 100644
--- a/app/javascript/mastodon/features/ui/components/embed_modal.js
+++ b/app/javascript/mastodon/features/ui/components/embed_modal.js
@@ -4,8 +4,8 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
 import { FormattedMessage, injectIntl } from 'react-intl';
 import api from '../../../api';
 
-@injectIntl
-export default class EmbedModal extends ImmutablePureComponent {
+export default @injectIntl
+class EmbedModal extends ImmutablePureComponent {
 
   static propTypes = {
     url: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/features/ui/components/focal_point_modal.js b/app/javascript/mastodon/features/ui/components/focal_point_modal.js
index 21bf6d81b..7488a3598 100644
--- a/app/javascript/mastodon/features/ui/components/focal_point_modal.js
+++ b/app/javascript/mastodon/features/ui/components/focal_point_modal.js
@@ -19,8 +19,8 @@ const mapDispatchToProps = (dispatch, { id }) => ({
 
 });
 
-@connect(mapStateToProps, mapDispatchToProps)
-export default class FocalPointModal extends ImmutablePureComponent {
+export default @connect(mapStateToProps, mapDispatchToProps)
+class FocalPointModal extends ImmutablePureComponent {
 
   static propTypes = {
     media: ImmutablePropTypes.map.isRequired,
diff --git a/app/javascript/mastodon/features/ui/components/media_modal.js b/app/javascript/mastodon/features/ui/components/media_modal.js
index 83b9e1b50..d29a4a6a7 100644
--- a/app/javascript/mastodon/features/ui/components/media_modal.js
+++ b/app/javascript/mastodon/features/ui/components/media_modal.js
@@ -18,8 +18,8 @@ const messages = defineMessages({
 
 export const previewState = 'previewMediaModal';
 
-@injectIntl
-export default class MediaModal extends ImmutablePureComponent {
+export default @injectIntl
+class MediaModal extends ImmutablePureComponent {
 
   static propTypes = {
     media: ImmutablePropTypes.list.isRequired,
@@ -149,7 +149,7 @@ export default class MediaModal extends ImmutablePureComponent {
             startTime={time || 0}
             onCloseVideo={onClose}
             detailed
-            description={image.get('description')}
+            alt={image.get('description')}
             key={image.get('url')}
           />
         );
diff --git a/app/javascript/mastodon/features/ui/components/mute_modal.js b/app/javascript/mastodon/features/ui/components/mute_modal.js
index 73e48cf09..ac356b42a 100644
--- a/app/javascript/mastodon/features/ui/components/mute_modal.js
+++ b/app/javascript/mastodon/features/ui/components/mute_modal.js
@@ -33,9 +33,9 @@ const mapDispatchToProps = dispatch => {
   };
 };
 
-@connect(mapStateToProps, mapDispatchToProps)
+export default @connect(mapStateToProps, mapDispatchToProps)
 @injectIntl
-export default class MuteModal extends React.PureComponent {
+class MuteModal extends React.PureComponent {
 
   static propTypes = {
     isSubmitting: PropTypes.bool.isRequired,
diff --git a/app/javascript/mastodon/features/ui/components/onboarding_modal.js b/app/javascript/mastodon/features/ui/components/onboarding_modal.js
index 9b713cf9e..25241bcd0 100644
--- a/app/javascript/mastodon/features/ui/components/onboarding_modal.js
+++ b/app/javascript/mastodon/features/ui/components/onboarding_modal.js
@@ -177,9 +177,9 @@ const mapStateToProps = state => ({
   domain: state.getIn(['meta', 'domain']),
 });
 
-@connect(mapStateToProps)
+export default @connect(mapStateToProps)
 @injectIntl
-export default class OnboardingModal extends React.PureComponent {
+class OnboardingModal extends React.PureComponent {
 
   static propTypes = {
     onClose: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/ui/components/report_modal.js b/app/javascript/mastodon/features/ui/components/report_modal.js
index 90f001319..bc6b18664 100644
--- a/app/javascript/mastodon/features/ui/components/report_modal.js
+++ b/app/javascript/mastodon/features/ui/components/report_modal.js
@@ -37,9 +37,9 @@ const makeMapStateToProps = () => {
   return mapStateToProps;
 };
 
-@connect(makeMapStateToProps)
+export default @connect(makeMapStateToProps)
 @injectIntl
-export default class ReportModal extends ImmutablePureComponent {
+class ReportModal extends ImmutablePureComponent {
 
   static propTypes = {
     isSubmitting: PropTypes.bool,
@@ -106,6 +106,7 @@ export default class ReportModal extends ImmutablePureComponent {
               onChange={this.handleCommentChange}
               onKeyDown={this.handleKeyDown}
               disabled={isSubmitting}
+              autoFocus
             />
 
             {domain && (
diff --git a/app/javascript/mastodon/features/ui/components/tabs_bar.js b/app/javascript/mastodon/features/ui/components/tabs_bar.js
index 60bc56eef..16236ea51 100644
--- a/app/javascript/mastodon/features/ui/components/tabs_bar.js
+++ b/app/javascript/mastodon/features/ui/components/tabs_bar.js
@@ -24,9 +24,9 @@ export function getLink (index) {
   return links[index].props.to;
 }
 
-@injectIntl
+export default @injectIntl
 @withRouter
-export default class TabsBar extends React.PureComponent {
+class TabsBar extends React.PureComponent {
 
   static propTypes = {
     intl: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/ui/components/video_modal.js b/app/javascript/mastodon/features/ui/components/video_modal.js
index 9ed4a43ad..7cf3eb4d4 100644
--- a/app/javascript/mastodon/features/ui/components/video_modal.js
+++ b/app/javascript/mastodon/features/ui/components/video_modal.js
@@ -24,7 +24,7 @@ export default class VideoModal extends ImmutablePureComponent {
             startTime={time}
             onCloseVideo={onClose}
             detailed
-            description={media.get('description')}
+            alt={media.get('description')}
           />
         </div>
       </div>
diff --git a/app/javascript/mastodon/features/ui/components/zoomable_image.js b/app/javascript/mastodon/features/ui/components/zoomable_image.js
index 0a0a4d41a..3f6562bc9 100644
--- a/app/javascript/mastodon/features/ui/components/zoomable_image.js
+++ b/app/javascript/mastodon/features/ui/components/zoomable_image.js
@@ -137,6 +137,7 @@ export default class ZoomableImage extends React.PureComponent {
           role='presentation'
           ref={this.setImageRef}
           alt={alt}
+          title={alt}
           src={src}
           style={{
             transform: `scale(${scale})`,
diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js
index 91eb37900..2781c5d64 100644
--- a/app/javascript/mastodon/features/ui/index.js
+++ b/app/javascript/mastodon/features/ui/index.js
@@ -187,10 +187,10 @@ class SwitchingColumnsArea extends React.PureComponent {
 
 }
 
-@connect(mapStateToProps)
+export default @connect(mapStateToProps)
 @injectIntl
 @withRouter
-export default class UI extends React.PureComponent {
+class UI extends React.PureComponent {
 
   static contextTypes = {
     router: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/ui/util/react_router_helpers.js b/app/javascript/mastodon/features/ui/util/react_router_helpers.js
index 32dfe320b..d452b871f 100644
--- a/app/javascript/mastodon/features/ui/util/react_router_helpers.js
+++ b/app/javascript/mastodon/features/ui/util/react_router_helpers.js
@@ -26,7 +26,7 @@ WrappedSwitch.propTypes = {
   children: PropTypes.node,
 };
 
-// Small Wraper to extract the params from the route and pass
+// Small Wrapper to extract the params from the route and pass
 // them to the rendered component, together with the content to
 // be rendered inside (the children)
 export class WrappedRoute extends React.Component {
diff --git a/app/javascript/mastodon/features/video/index.js b/app/javascript/mastodon/features/video/index.js
index 52b395f88..d17253957 100644
--- a/app/javascript/mastodon/features/video/index.js
+++ b/app/javascript/mastodon/features/video/index.js
@@ -84,8 +84,8 @@ export const getPointerPosition = (el, event) => {
   return position;
 };
 
-@injectIntl
-export default class Video extends React.PureComponent {
+export default @injectIntl
+class Video extends React.PureComponent {
 
   static propTypes = {
     preview: PropTypes.string,
@@ -252,11 +252,12 @@ export default class Video extends React.PureComponent {
   }
 
   handleOpenVideo = () => {
-    const { src, preview, width, height } = this.props;
+    const { src, preview, width, height, alt } = this.props;
     const media = fromJS({
       type: 'video',
       url: src,
       preview_url: preview,
+      description: alt,
       width,
       height,
     });
diff --git a/app/javascript/mastodon/initial_state.js b/app/javascript/mastodon/initial_state.js
index 8751a5636..4c6922f9b 100644
--- a/app/javascript/mastodon/initial_state.js
+++ b/app/javascript/mastodon/initial_state.js
@@ -11,7 +11,7 @@ export const boostModal = getMeta('boost_modal');
 export const deleteModal = getMeta('delete_modal');
 export const me = getMeta('me');
 export const searchEnabled = getMeta('search_enabled');
-export const maxChars = getMeta('max_toot_chars') || 500;
+export const maxChars = (initialState && initialState.max_toot_chars) || 500;
 export const invitesEnabled = getMeta('invites_enabled');
 export const version = getMeta('version');
 
diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index 5478e2d3e..56d78ae64 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -10,11 +10,12 @@
   "account.endorse": "إبرازه على الملف الشخصي",
   "account.follow": "تابِع",
   "account.followers": "المتابعون",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "لا أحد يتبع هذا الحساب بعد.",
   "account.follows": "يتبع",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "هذا المستخدِم لا يتبع أحدًا بعد.",
   "account.follows_you": "يتابعك",
   "account.hide_reblogs": "إخفاء ترقيات @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "وسائط",
   "account.mention": "أُذكُر @{name}",
   "account.moved_to": "{name} إنتقل إلى :",
@@ -89,7 +90,7 @@
   "confirmations.mute.confirm": "أكتم",
   "confirmations.mute.message": "هل أنت متأكد أنك تريد كتم {name} ؟",
   "confirmations.redraft.confirm": "إزالة و إعادة الصياغة",
-  "confirmations.redraft.message": "هل أنت متأكد من أنك تريد حذف هذا المنشور و إعادة صياغته ؟ سوف تفقد جميع الردود و الترقيات و المفضلة المتصلة به.",
+  "confirmations.redraft.message": "هل أنت متأكد من أنك تريد حذف هذا المنشور و إعادة صياغته ؟ سوف تفقد جميع الإعجابات و الترقيات أما الردود المتصلة به فستُصبِح يتيمة.",
   "confirmations.unfollow.confirm": "إلغاء المتابعة",
   "confirmations.unfollow.message": "متأكد من أنك تريد إلغاء متابعة {name} ؟",
   "embed.instructions": "يمكنكم إدماج هذا المنشور على موقعكم الإلكتروني عن طريق نسخ الشفرة أدناه.",
@@ -108,10 +109,10 @@
   "emoji_button.search_results": "نتائج البحث",
   "emoji_button.symbols": "رموز",
   "emoji_button.travel": "أماكن و أسفار",
-  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.blocks": "لم تقم بحظر أي مستخدِم بعد.",
   "empty_column.community": "الخط الزمني المحلي فارغ. أكتب شيئا ما للعامة كبداية !",
   "empty_column.direct": "لم تتلق أية رسالة خاصة مباشِرة بعد. سوف يتم عرض الرسائل المباشرة هنا إن قمت بإرسال واحدة أو تلقيت البعض منها.",
-  "empty_column.domain_blocks": "There are no hidden domains yet.",
+  "empty_column.domain_blocks": "ليس هناك نطاقات مخفية بعد.",
   "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.",
@@ -119,8 +120,8 @@
   "empty_column.home": "إنّ الخيط الزمني لصفحتك الرئيسية فارغ. قم بزيارة {public} أو استخدم حقل البحث لكي تكتشف مستخدمين آخرين.",
   "empty_column.home.public_timeline": "الخيط العام",
   "empty_column.list": "هذه القائمة فارغة مؤقتا و لكن سوف تمتلئ تدريجيا عندما يبدأ الأعضاء المُنتَمين إليها بنشر تبويقات.",
-  "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.lists": "ليس عندك أية قائمة بعد. سوف تظهر قائمتك هنا إن قمت بإنشاء واحدة.",
+  "empty_column.mutes": "لم تقم بكتم أي مستخدم بعد.",
   "empty_column.notifications": "لم تتلق أي إشعار بعدُ. تفاعل مع المستخدمين الآخرين لإنشاء محادثة.",
   "empty_column.public": "لا يوجد أي شيء هنا ! قم بنشر شيء ما للعامة، أو إتبع مستخدمين آخرين في الخوادم المثيلة الأخرى لملء خيط المحادثات العام",
   "follow_request.authorize": "ترخيص",
@@ -137,30 +138,30 @@
   "home.column_settings.show_reblogs": "عرض الترقيات",
   "home.column_settings.show_replies": "عرض الردود",
   "keyboard_shortcuts.back": "للعودة",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "لفتح قائمة المستخدمين المحظورين",
   "keyboard_shortcuts.boost": "للترقية",
   "keyboard_shortcuts.column": "للتركيز على منشور على أحد الأعمدة",
   "keyboard_shortcuts.compose": "للتركيز على نافذة تحرير النصوص",
   "keyboard_shortcuts.description": "Description",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "لفتح عمود الرسائل المباشرة",
   "keyboard_shortcuts.down": "للإنتقال إلى أسفل القائمة",
   "keyboard_shortcuts.enter": "to open status",
   "keyboard_shortcuts.favourite": "للإضافة إلى المفضلة",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "لفتح قائمة المفضلات",
+  "keyboard_shortcuts.federated": "لفتح الخيط الزمني الفديرالي",
   "keyboard_shortcuts.heading": "Keyboard Shortcuts",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "لفتح الخيط الرئيسي",
   "keyboard_shortcuts.hotkey": "مفتاح الإختصار",
   "keyboard_shortcuts.legend": "لعرض هذا المفتاح",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "لفتح الخيط الزمني المحلي",
   "keyboard_shortcuts.mention": "لذِكر الناشر",
-  "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.pinned": "to open pinned toots list",
+  "keyboard_shortcuts.muted": "لفتح قائمة المستخدِمين المكتومين",
+  "keyboard_shortcuts.my_profile": "لفتح ملفك الشخصي",
+  "keyboard_shortcuts.notifications": "لفتح عمود الإشعارات",
+  "keyboard_shortcuts.pinned": "لفتح قائمة التبويقات المدبسة",
   "keyboard_shortcuts.profile": "لفتح رابط الناشر",
   "keyboard_shortcuts.reply": "للردّ",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "لفتح قائمة طلبات المتابعة",
   "keyboard_shortcuts.search": "للتركيز على البحث",
   "keyboard_shortcuts.start": "to open \"get started\" column",
   "keyboard_shortcuts.toggle_hidden": "لعرض أو إخفاء النص مِن وراء التحذير",
@@ -183,10 +184,10 @@
   "missing_indicator.label": "تعذر العثور عليه",
   "missing_indicator.sublabel": "تعذر العثور على هذا المورد",
   "mute_modal.hide_notifications": "هل تود إخفاء الإخطارات القادمة من هذا المستخدم ؟",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "تطبيقات الأجهزة المحمولة",
   "navigation_bar.blocks": "الحسابات المحجوبة",
   "navigation_bar.community_timeline": "الخيط العام المحلي",
-  "navigation_bar.compose": "Compose new toot",
+  "navigation_bar.compose": "تحرير تبويق جديد",
   "navigation_bar.direct": "الرسائل المباشِرة",
   "navigation_bar.discover": "إكتشف",
   "navigation_bar.domain_blocks": "النطاقات المخفية",
@@ -297,7 +298,7 @@
   "status.reblog": "رَقِّي",
   "status.reblog_private": "القيام بالترقية إلى الجمهور الأصلي",
   "status.reblogged_by": "رقّاه {name}",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
+  "status.reblogs.empty": "لم يقم أي أحد بترقية هذا التبويق بعد. عندما يقوم أحدهم بذلك سوف تظهر هنا.",
   "status.redraft": "إزالة و إعادة الصياغة",
   "status.reply": "ردّ",
   "status.replyAll": "رُد على الخيط",
@@ -319,7 +320,7 @@
   "trends.count_by_accounts": "{count} {rawCount, plural, one {person} آخرون {people}} يتحدثون",
   "ui.beforeunload": "سوف تفقد مسودتك إن تركت ماستدون.",
   "upload_area.title": "إسحب ثم أفلت للرفع",
-  "upload_button.label": "إضافة وسائط",
+  "upload_button.label": "إضافة وسائط (JPEG، PNG، GIF، WebM، MP4، MOV)",
   "upload_form.description": "وصف للمعاقين بصريا",
   "upload_form.focus": "قص",
   "upload_form.undo": "حذف",
diff --git a/app/javascript/mastodon/locales/ast.json b/app/javascript/mastodon/locales/ast.json
index da1ab62e6..e3cfabee3 100644
--- a/app/javascript/mastodon/locales/ast.json
+++ b/app/javascript/mastodon/locales/ast.json
@@ -1,174 +1,175 @@
 {
-  "account.badges.bot": "Bot",
-  "account.block": "Block @{name}",
+  "account.badges.bot": "Robó",
+  "account.block": "Bloquiar a @{name}",
   "account.block_domain": "Hide everything from {domain}",
   "account.blocked": "Blocked",
-  "account.direct": "Direct message @{name}",
+  "account.direct": "Unviar un mensaxe direutu a @{name}",
   "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
   "account.domain_blocked": "Domain hidden",
-  "account.edit_profile": "Edit profile",
+  "account.edit_profile": "Editar perfil",
   "account.endorse": "Feature on profile",
   "account.follow": "Follow",
-  "account.followers": "Followers",
+  "account.followers": "Siguidores",
   "account.followers.empty": "No one follows this user yet.",
   "account.follows": "Follows",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "Esti usuariu entá nun sigue a naide.",
   "account.follows_you": "Follows you",
   "account.hide_reblogs": "Hide boosts from @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Media",
-  "account.mention": "Mention @{name}",
+  "account.mention": "Mentar a @{name}",
   "account.moved_to": "{name} has moved to:",
-  "account.mute": "Mute @{name}",
+  "account.mute": "Silenciar a @{name}",
   "account.mute_notifications": "Mute notifications from @{name}",
   "account.muted": "Muted",
   "account.posts": "Toots",
-  "account.posts_with_replies": "Toots and replies",
+  "account.posts_with_replies": "Toots y rempuestes",
   "account.report": "Report @{name}",
   "account.requested": "Awaiting approval. Click to cancel follow request",
   "account.share": "Share @{name}'s profile",
   "account.show_reblogs": "Show boosts from @{name}",
-  "account.unblock": "Unblock @{name}",
+  "account.unblock": "Desbloquiar a @{name}",
   "account.unblock_domain": "Unhide {domain}",
   "account.unendorse": "Don't feature on profile",
   "account.unfollow": "Unfollow",
   "account.unmute": "Unmute @{name}",
   "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "View full profile",
-  "alert.unexpected.message": "An unexpected error occurred.",
-  "alert.unexpected.title": "Oops!",
-  "boost_modal.combo": "You can press {combo} to skip this next time",
+  "alert.unexpected.message": "Asocedió un fallu inesperáu.",
+  "alert.unexpected.title": "¡Ups!",
+  "boost_modal.combo": "Pues primir {combo} pa saltar esto la próxima vegada",
   "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.blocks": "Usuarios bloquiaos",
   "column.community": "Local timeline",
-  "column.direct": "Direct messages",
-  "column.domain_blocks": "Hidden 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.direct": "Mensaxes direutos",
+  "column.domain_blocks": "Dominios anubríos",
+  "column.favourites": "Favoritos",
+  "column.follow_requests": "Solicitúes de siguimientu",
+  "column.home": "Aniciu",
+  "column.lists": "Llistes",
+  "column.mutes": "Usuarios silenciaos",
+  "column.notifications": "Avisos",
+  "column.pins": "Toots fixaos",
+  "column.public": "Llinia temporal federada",
+  "column_back_button.label": "Atrás",
   "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.moveLeft_settings": "Mover la columna a la esquierda",
+  "column_header.moveRight_settings": "Mover la columna a la drecha",
   "column_header.pin": "Pin",
   "column_header.show_settings": "Show settings",
-  "column_header.unpin": "Unpin",
-  "column_subheading.settings": "Settings",
+  "column_header.unpin": "Desfixar",
+  "column_subheading.settings": "Axustes",
   "community.column_settings.media_only": "Media Only",
-  "compose_form.direct_message_warning": "This toot will only be sent to all the mentioned users.",
+  "compose_form.direct_message_warning": "Esti toot namái va unviase a los usuarios mentaos.",
   "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.placeholder": "¿En qué pienses?",
   "compose_form.publish": "Toot",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.marked": "Media is marked as sensitive",
   "compose_form.sensitive.unmarked": "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",
+  "compose_form.spoiler.marked": "El testu nun va anubrise darrera d'una alvertencia",
+  "compose_form.spoiler.unmarked": "El testu va anubrise",
+  "compose_form.spoiler_placeholder": "Escribi equí l'avertencia",
+  "confirmation_modal.cancel": "Encaboxar",
   "confirmations.block.confirm": "Block",
-  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.block.message": "¿De xuru que quies bloquiar a {name}?",
   "confirmations.delete.confirm": "Delete",
-  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.delete.message": "¿De xuru que quies desaniciar esti estáu?",
   "confirmations.delete_list.confirm": "Delete",
-  "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
+  "confirmations.delete_list.message": "¿De xuru que quies desaniciar dafechu esta llista?",
   "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.mute.confirm": "Mute",
-  "confirmations.mute.message": "Are you sure you want to mute {name}?",
+  "confirmations.mute.message": "¿De xuru que quies silenciar a {name}?",
   "confirmations.redraft.confirm": "Delete & redraft",
   "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.",
   "confirmations.unfollow.confirm": "Unfollow",
   "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
   "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.activity": "Actividá",
   "emoji_button.custom": "Custom",
-  "emoji_button.flags": "Flags",
-  "emoji_button.food": "Food & Drink",
+  "emoji_button.flags": "Banderes",
+  "emoji_button.food": "Comída y bébora",
   "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.nature": "Natura",
+  "emoji_button.not_found": "¡Nun hai fustaxes! (╯°□°)╯︵ ┻━┻",
+  "emoji_button.objects": "Oxetos",
+  "emoji_button.people": "Xente",
   "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.blocks": "You haven't blocked any users yet.",
+  "emoji_button.symbols": "Símbolos",
+  "emoji_button.travel": "Viaxes y llugares",
+  "empty_column.blocks": "Entá nun bloquiesti a dengún usuariu.",
   "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 hidden domains yet.",
+  "empty_column.direct": "Entá nun tienes dengún mensaxe direutu. Cuando unvies o recibas dalgún, va apaecer equí",
+  "empty_column.domain_blocks": "Entá nun hai dominios anubríos.",
   "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.follow_requests": "Entá nun tienes denguna solicitú de siguimientu. Cuando recibas una, va amosase equí.",
   "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.home.public_timeline": "la llinia temporal pública",
   "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.lists": "Entá nun tienes denguna llista. Cuando crees una, va amosase equí.",
+  "empty_column.mutes": "Enta nun silenciesti a dengún usuariu.",
+  "empty_column.notifications": "Entá nun tienes dengún avisu. Interactua con otros p'aniciar la conversación.",
   "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
-  "follow_request.authorize": "Authorize",
-  "follow_request.reject": "Reject",
-  "getting_started.developers": "Developers",
-  "getting_started.documentation": "Documentation",
-  "getting_started.find_friends": "Find friends from Twitter",
+  "follow_request.authorize": "Autorizar",
+  "follow_request.reject": "Refugar",
+  "getting_started.developers": "Desendolcadores",
+  "getting_started.documentation": "Documentación",
+  "getting_started.find_friends": "Alcontrar collacios de Twitter",
   "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",
+  "getting_started.invite": "Convidar xente",
+  "getting_started.open_source_notice": "Mastodon ye software de códigu abiertu. Pues collaborar o informar de fallos en {github} (GitHub).",
+  "getting_started.security": "Seguranza",
+  "getting_started.terms": "Términos del serviciu",
   "home.column_settings.basic": "Basic",
   "home.column_settings.show_reblogs": "Show boosts",
   "home.column_settings.show_replies": "Show replies",
-  "keyboard_shortcuts.back": "to navigate back",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.back": "pa dir p'atrás",
+  "keyboard_shortcuts.blocked": "p'abrir la llista d'usuarios bloquiaos",
   "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.description": "Descripción",
+  "keyboard_shortcuts.direct": "p'abrir la columna de los mensaxes direutos",
+  "keyboard_shortcuts.down": "pa baxar na llista",
   "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.favourites": "p'abrir la llista de favoritos",
+  "keyboard_shortcuts.federated": "p'abrir la llinia temporal federada",
+  "keyboard_shortcuts.heading": "Atayos del tecláu",
+  "keyboard_shortcuts.home": "p'abrir la llinia temporal d'aniciu",
+  "keyboard_shortcuts.hotkey": "Atayu",
+  "keyboard_shortcuts.legend": "p'amosar esta lleenda",
+  "keyboard_shortcuts.local": "p'abrir la llinia temporal llocal",
+  "keyboard_shortcuts.mention": "pa mentar al autor",
+  "keyboard_shortcuts.muted": "p'abrir la llista d'usuarios silenciaos",
   "keyboard_shortcuts.my_profile": "to open your profile",
-  "keyboard_shortcuts.notifications": "to open notifications column",
-  "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.notifications": "p'abrir la columna d'avisos",
+  "keyboard_shortcuts.pinned": "p'abrir la llista de toots fixaos",
+  "keyboard_shortcuts.profile": "p'abrir el perfil del autor",
+  "keyboard_shortcuts.reply": "pa responder",
+  "keyboard_shortcuts.requests": "p'abrir la llista de solicitúes de siguimientu",
   "keyboard_shortcuts.search": "to focus search",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
+  "keyboard_shortcuts.start": "p'abrir la columna «entamar»",
   "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW",
-  "keyboard_shortcuts.toot": "to start a brand new toot",
+  "keyboard_shortcuts.toot": "p'apenzar un toot nuevu",
   "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
-  "keyboard_shortcuts.up": "to move up in the list",
+  "keyboard_shortcuts.up": "pa xubir na llista",
   "lightbox.close": "Close",
-  "lightbox.next": "Next",
+  "lightbox.next": "Siguiente",
   "lightbox.previous": "Previous",
   "lists.account.add": "Add to list",
   "lists.account.remove": "Remove from list",
@@ -180,55 +181,55 @@
   "lists.subheading": "Your lists",
   "loading_indicator.label": "Loading...",
   "media_gallery.toggle_visible": "Toggle visibility",
-  "missing_indicator.label": "Not found",
-  "missing_indicator.sublabel": "This resource could not be found",
+  "missing_indicator.label": "Nun s'alcontró",
+  "missing_indicator.sublabel": "Esti recursu nun pudo alcontrase",
   "mute_modal.hide_notifications": "Hide notifications from this user?",
-  "navigation_bar.apps": "Mobile apps",
-  "navigation_bar.blocks": "Blocked users",
+  "navigation_bar.apps": "Aplicaciones móviles",
+  "navigation_bar.blocks": "Usuarios bloquiaos",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new toot",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Mensaxes direutos",
   "navigation_bar.discover": "Discover",
-  "navigation_bar.domain_blocks": "Hidden domains",
-  "navigation_bar.edit_profile": "Edit profile",
-  "navigation_bar.favourites": "Favourites",
+  "navigation_bar.domain_blocks": "Dominios anubríos",
+  "navigation_bar.edit_profile": "Editar perfil",
+  "navigation_bar.favourites": "Favoritos",
   "navigation_bar.filters": "Muted words",
-  "navigation_bar.follow_requests": "Follow requests",
-  "navigation_bar.info": "About this instance",
-  "navigation_bar.keyboard_shortcuts": "Hotkeys",
+  "navigation_bar.follow_requests": "Solicitúes de siguimientu",
+  "navigation_bar.info": "Tocante a esta instancia",
+  "navigation_bar.keyboard_shortcuts": "Atayos",
   "navigation_bar.lists": "Lists",
-  "navigation_bar.logout": "Logout",
-  "navigation_bar.mutes": "Muted users",
+  "navigation_bar.logout": "Zarrar sesión",
+  "navigation_bar.mutes": "Usuarios silenciaos",
   "navigation_bar.personal": "Personal",
-  "navigation_bar.pins": "Pinned toots",
-  "navigation_bar.preferences": "Preferences",
-  "navigation_bar.public_timeline": "Federated timeline",
-  "navigation_bar.security": "Security",
+  "navigation_bar.pins": "Toots fixaos",
+  "navigation_bar.preferences": "Preferencies",
+  "navigation_bar.public_timeline": "Llinia temporal federada",
+  "navigation_bar.security": "Seguranza",
   "notification.favourite": "{name} favourited your status",
-  "notification.follow": "{name} followed you",
-  "notification.mention": "{name} mentioned you",
+  "notification.follow": "{name} siguióte",
+  "notification.mention": "{name} mentóte",
   "notification.reblog": "{name} boosted your status",
-  "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.clear": "Llimpiar avisos",
+  "notifications.clear_confirmation": "¿De xuru que quies llimpiar dafechu tolos avisos?",
+  "notifications.column_settings.alert": "Avisos d'escritoriu",
   "notifications.column_settings.favourite": "Favourites:",
-  "notifications.column_settings.follow": "New followers:",
-  "notifications.column_settings.mention": "Mentions:",
+  "notifications.column_settings.follow": "Siguidores nuevos:",
+  "notifications.column_settings.mention": "Menciones:",
   "notifications.column_settings.push": "Push notifications",
-  "notifications.column_settings.push_meta": "This device",
+  "notifications.column_settings.push_meta": "Esti preséu",
   "notifications.column_settings.reblog": "Boosts:",
-  "notifications.column_settings.show": "Show in column",
-  "notifications.column_settings.sound": "Play sound",
-  "notifications.group": "{count} notifications",
-  "onboarding.done": "Done",
-  "onboarding.next": "Next",
+  "notifications.column_settings.show": "Amosar en columna",
+  "notifications.column_settings.sound": "Reproducir soníu",
+  "notifications.group": "{count} avisos",
+  "onboarding.done": "Fecho",
+  "onboarding.next": "Siguiente",
   "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
-  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.home": "La llinia temporal d'aniciu amuesa artículos de xente a la que sigues.",
   "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
-  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.federation": "Mastodon ye una rede de sividores independientes xuníos pa facer una rede social grande. Nós llamamos instancies a esos sirvidores.",
   "onboarding.page_one.full_handle": "Your full handle",
   "onboarding.page_one.handle_hint": "This is what you would tell your friends to search for.",
-  "onboarding.page_one.welcome": "Welcome to Mastodon!",
+  "onboarding.page_one.welcome": "¡Afáyate en Mastodon!",
   "onboarding.page_six.admin": "Your instance's admin is {admin}.",
   "onboarding.page_six.almost_done": "Almost done...",
   "onboarding.page_six.appetoot": "Bon Appetoot!",
@@ -236,7 +237,7 @@
   "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
   "onboarding.page_six.guidelines": "community guidelines",
   "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
-  "onboarding.page_six.various_app": "mobile apps",
+  "onboarding.page_six.various_app": "aplicaciones móviles",
   "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
   "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
   "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
@@ -245,7 +246,7 @@
   "privacy.direct.long": "Post to mentioned users only",
   "privacy.direct.short": "Direct",
   "privacy.private.long": "Post to followers only",
-  "privacy.private.short": "Followers-only",
+  "privacy.private.short": "Namái siguidores",
   "privacy.public.long": "Post to public timelines",
   "privacy.public.short": "Public",
   "privacy.unlisted.long": "Do not show in public timelines",
@@ -254,44 +255,44 @@
   "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.just_now": "agora",
   "relative_time.minutes": "{number}m",
   "relative_time.seconds": "{number}s",
-  "reply_indicator.cancel": "Cancel",
+  "reply_indicator.cancel": "Encaboxar",
   "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 instance moderators. You can provide an explanation of why you are reporting this account below:",
-  "report.placeholder": "Additional comments",
+  "report.placeholder": "Comentarios adicionales",
   "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.hashtag": "etiqueta",
+  "search_popout.tips.status": "estáu",
   "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
-  "search_popout.tips.user": "user",
-  "search_results.accounts": "People",
+  "search_popout.tips.user": "usuariu",
+  "search_results.accounts": "Xente",
   "search_results.hashtags": "Hashtags",
   "search_results.statuses": "Toots",
   "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
   "standalone.public_title": "A look inside...",
-  "status.block": "Block @{name}",
+  "status.block": "Bloquiar a @{name}",
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
-  "status.embed": "Embed",
+  "status.direct": "Unviar un mensaxe direutu a @{name}",
+  "status.embed": "Empotrar",
   "status.favourite": "Favourite",
   "status.filtered": "Filtered",
-  "status.load_more": "Load more",
+  "status.load_more": "Cargar más",
   "status.media_hidden": "Media hidden",
-  "status.mention": "Mention @{name}",
-  "status.more": "More",
-  "status.mute": "Mute @{name}",
+  "status.mention": "Mentar a @{name}",
+  "status.more": "Más",
+  "status.mute": "Silenciar a @{name}",
   "status.mute_conversation": "Mute conversation",
-  "status.open": "Expand this status",
+  "status.open": "Espander esti estáu",
   "status.pin": "Pin on profile",
   "status.pinned": "Pinned toot",
   "status.reblog": "Boost",
@@ -299,25 +300,25 @@
   "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.reply": "Reply",
+  "status.reply": "Responder",
   "status.replyAll": "Reply to thread",
   "status.report": "Report @{name}",
   "status.sensitive_toggle": "Click to view",
   "status.sensitive_warning": "Sensitive content",
   "status.share": "Share",
-  "status.show_less": "Show less",
+  "status.show_less": "Amosar menos",
   "status.show_less_all": "Show less for all",
-  "status.show_more": "Show more",
+  "status.show_more": "Amosar más",
   "status.show_more_all": "Show more for all",
   "status.unmute_conversation": "Unmute conversation",
   "status.unpin": "Unpin from profile",
   "tabs_bar.federated_timeline": "Federated",
-  "tabs_bar.home": "Home",
+  "tabs_bar.home": "Aniciu",
   "tabs_bar.local_timeline": "Local",
-  "tabs_bar.notifications": "Notifications",
+  "tabs_bar.notifications": "Avisos",
   "tabs_bar.search": "Search",
   "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking",
-  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
+  "ui.beforeunload": "El borrador va perdese si coles de Mastodon.",
   "upload_area.title": "Drag & drop to upload",
   "upload_button.label": "Add media",
   "upload_form.description": "Describe for the visually impaired",
diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json
index 725bbed41..5730c1354 100644
--- a/app/javascript/mastodon/locales/bg.json
+++ b/app/javascript/mastodon/locales/bg.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "Твой последовател",
   "account.hide_reblogs": "Hide boosts from @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Media",
   "account.mention": "Споменаване",
   "account.moved_to": "{name} has moved to:",
diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index b7527d24e..0364d0bba 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -7,14 +7,15 @@
   "account.disclaimer_full": "La informació següent pot reflectir incompleta el perfil de l'usuari.",
   "account.domain_blocked": "Domini ocult",
   "account.edit_profile": "Editar el perfil",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "Característica del perfil",
   "account.follow": "Segueix",
   "account.followers": "Seguidors",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "Encara ningú no segueix aquest usuari.",
   "account.follows": "Seguint",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "Aquest usuari encara no segueix a ningú.",
   "account.follows_you": "Et segueix",
   "account.hide_reblogs": "Amaga els impulsos de @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Media",
   "account.mention": "Esmentar @{name}",
   "account.moved_to": "{name} s'ha mogut a:",
@@ -29,7 +30,7 @@
   "account.show_reblogs": "Mostra els impulsos de @{name}",
   "account.unblock": "Desbloca @{name}",
   "account.unblock_domain": "Mostra {domain}",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "No es mostren al perfil",
   "account.unfollow": "Deixa de seguir",
   "account.unmute": "Treure silenci de @{name}",
   "account.unmute_notifications": "Activar notificacions de @{name}",
@@ -89,7 +90,7 @@
   "confirmations.mute.confirm": "Silencia",
   "confirmations.mute.message": "Estàs segur que vols silenciar {name}?",
   "confirmations.redraft.confirm": "Esborrar i refer",
-  "confirmations.redraft.message": "Estàs segur que vols esborrar aquesta publicació i tornar a redactar-la? Perderàs totes les respostes, impulsos i favorits.",
+  "confirmations.redraft.message": "Estàs segur que vols esborrar aquesta publicació i tornar a redactar-la? Perderàs totes els impulsos i favorits, i les respostes a la publicació original es quedaran orfes.",
   "confirmations.unfollow.confirm": "Deixa de seguir",
   "confirmations.unfollow.message": "Estàs segur que vols deixar de seguir {name}?",
   "embed.instructions": "Incrusta aquest estat al lloc web copiant el codi a continuació.",
@@ -108,19 +109,19 @@
   "emoji_button.search_results": "Resultats de la cerca",
   "emoji_button.symbols": "Símbols",
   "emoji_button.travel": "Viatges i Llocs",
-  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.blocks": "Encara no has bloquejat cap usuari.",
   "empty_column.community": "La línia de temps local és buida. Escriu alguna cosa públicament per fer rodar la pilota!",
   "empty_column.direct": "Encara no tens missatges directes. Quan enviïs o rebis un, es mostrarà aquí.",
-  "empty_column.domain_blocks": "There are no hidden 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.domain_blocks": "Encara no hi ha dominis ocults.",
+  "empty_column.favourited_statuses": "Encara no tens cap toot favorit. Quan en tinguis, apareixerà aquí.",
+  "empty_column.favourites": "Encara ningú ha marcat aquest toot com a favorit. Quan algú ho faci, apareixera aquí.",
+  "empty_column.follow_requests": "Encara no teniu cap petició de seguiment. Quan rebeu una, apareixerà aquí.",
   "empty_column.hashtag": "Encara no hi ha res amb aquesta etiqueta.",
   "empty_column.home": "Encara no segueixes ningú. Visita {public} o fes cerca per començar i conèixer altres usuaris.",
   "empty_column.home.public_timeline": "la línia de temps pública",
   "empty_column.list": "Encara no hi ha res en aquesta llista. Quan els membres d'aquesta llista publiquin nous estats, apareixeran aquí.",
-  "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.lists": "Encara no tens cap llista. Quan en facis una, apareixerà aquí.",
+  "empty_column.mutes": "Encara no has silenciat cap usuari.",
   "empty_column.notifications": "Encara no tens notificacions. Interactua amb altres per iniciar la conversa.",
   "empty_column.public": "No hi ha res aquí! Escriu alguna cosa públicament o segueix manualment usuaris d'altres instàncies per omplir-ho",
   "follow_request.authorize": "Autoritzar",
@@ -137,32 +138,32 @@
   "home.column_settings.show_reblogs": "Mostrar impulsos",
   "home.column_settings.show_replies": "Mostrar respostes",
   "keyboard_shortcuts.back": "navegar enrera",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "per obrir la llista d'usuaris bloquejats",
   "keyboard_shortcuts.boost": "impulsar",
   "keyboard_shortcuts.column": "per centrar un estat en una de les columnes",
   "keyboard_shortcuts.compose": "per centrar l'area de composició de text",
   "keyboard_shortcuts.description": "Description",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "per obrir la columna de missatges directes",
   "keyboard_shortcuts.down": "per baixar en la llista",
   "keyboard_shortcuts.enter": "ampliar estat",
   "keyboard_shortcuts.favourite": "afavorir",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "per obrir la llista de favorits",
+  "keyboard_shortcuts.federated": "per obrir la línia de temps federada",
   "keyboard_shortcuts.heading": "Keyboard Shortcuts",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "per obrir la línia de temps Inici",
   "keyboard_shortcuts.hotkey": "Tecla d'accés directe",
   "keyboard_shortcuts.legend": "per a mostrar aquesta llegenda",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "per obrir la línia de temps local",
   "keyboard_shortcuts.mention": "per esmentar l'autor",
-  "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.pinned": "to open pinned toots list",
-  "keyboard_shortcuts.profile": "to open author's profile",
+  "keyboard_shortcuts.muted": "per obrir la llista d'usuaris silenciats",
+  "keyboard_shortcuts.my_profile": "per obrir el teu perfil",
+  "keyboard_shortcuts.notifications": "per obrir la columna de notificacions",
+  "keyboard_shortcuts.pinned": "per obrir la llista de toots fixats",
+  "keyboard_shortcuts.profile": "per obrir el perfil de l'autor",
   "keyboard_shortcuts.reply": "respondre",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "per obrir la llista de sol·licituds de seguiment",
   "keyboard_shortcuts.search": "per centrar la cerca",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
+  "keyboard_shortcuts.start": "per obrir la columna \"Començar\"",
   "keyboard_shortcuts.toggle_hidden": "per a mostrar/amagar text sota CW",
   "keyboard_shortcuts.toot": "per a començar un toot nou de trinca",
   "keyboard_shortcuts.unfocus": "descentrar l'area de composició de text/cerca",
@@ -183,16 +184,16 @@
   "missing_indicator.label": "No trobat",
   "missing_indicator.sublabel": "Aquest recurs no pot ser trobat",
   "mute_modal.hide_notifications": "Amagar notificacions d'aquest usuari?",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "Apps Mòbils",
   "navigation_bar.blocks": "Usuaris bloquejats",
   "navigation_bar.community_timeline": "Línia de temps Local",
-  "navigation_bar.compose": "Compose new toot",
+  "navigation_bar.compose": "Redacta nou toot",
   "navigation_bar.direct": "Missatges directes",
   "navigation_bar.discover": "Descobreix",
   "navigation_bar.domain_blocks": "Dominis ocults",
   "navigation_bar.edit_profile": "Editar perfil",
   "navigation_bar.favourites": "Favorits",
-  "navigation_bar.filters": "Muted words",
+  "navigation_bar.filters": "Paraules silenciades",
   "navigation_bar.follow_requests": "Sol·licituds de seguiment",
   "navigation_bar.info": "Informació addicional",
   "navigation_bar.keyboard_shortcuts": "Dreceres de teclat",
@@ -280,11 +281,11 @@
   "status.cancel_reblog_private": "Desfer l'impuls",
   "status.cannot_reblog": "Aquesta publicació no pot ser retootejada",
   "status.delete": "Esborrar",
-  "status.detailed_status": "Detailed conversation view",
+  "status.detailed_status": "Visualització detallada de la conversa",
   "status.direct": "Missatge directe @{name}",
   "status.embed": "Incrustar",
   "status.favourite": "Favorit",
-  "status.filtered": "Filtered",
+  "status.filtered": "Filtrat",
   "status.load_more": "Carrega més",
   "status.media_hidden": "Multimèdia amagat",
   "status.mention": "Esmentar @{name}",
@@ -297,7 +298,7 @@
   "status.reblog": "Impuls",
   "status.reblog_private": "Impulsar a l'audiència original",
   "status.reblogged_by": "{name} ha retootejat",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
+  "status.reblogs.empty": "Encara ningú no ha impulsat aquest toot. Quan algú ho faci, apareixeran aquí.",
   "status.redraft": "Esborrar i reescriure",
   "status.reply": "Respondre",
   "status.replyAll": "Respondre al tema",
@@ -319,7 +320,7 @@
   "trends.count_by_accounts": "{count} {rawCount, plural, una {person} altres {people}} parlant",
   "ui.beforeunload": "El vostre esborrany es perdrà si sortiu de Mastodon.",
   "upload_area.title": "Arrossega i deixa anar per carregar",
-  "upload_button.label": "Afegir multimèdia",
+  "upload_button.label": "Afegir multimèdia (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_form.description": "Descriure els problemes visuals",
   "upload_form.focus": "Retallar",
   "upload_form.undo": "Esborra",
diff --git a/app/javascript/mastodon/locales/co.json b/app/javascript/mastodon/locales/co.json
index 195c2a566..8bf083235 100644
--- a/app/javascript/mastodon/locales/co.json
+++ b/app/javascript/mastodon/locales/co.json
@@ -10,11 +10,12 @@
   "account.endorse": "Fà figurà nant'à u prufilu",
   "account.follow": "Siguità",
   "account.followers": "Abbunati",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "Nisunu hè abbunatu à st'utilizatore.",
   "account.follows": "Abbunamenti",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "St'utilizatore ùn seguita nisunu.",
   "account.follows_you": "Vi seguita",
   "account.hide_reblogs": "Piattà spartere da @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Media",
   "account.mention": "Mintuvà @{name}",
   "account.moved_to": "{name} hè partutu nant'à:",
@@ -108,19 +109,19 @@
   "emoji_button.search_results": "Risultati di a cerca",
   "emoji_button.symbols": "Simbuli",
   "emoji_button.travel": "Lochi è Viaghju",
-  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.blocks": "Per avà ùn avete bluccatu manc'un utilizatore.",
   "empty_column.community": "Ùn c'hè nunda indè a linea lucale. Scrivete puru qualcosa!",
   "empty_column.direct": "Ùn avete ancu nisun missaghju direttu. S'è voi mandate o ricevete unu, u vidarete quì.",
-  "empty_column.domain_blocks": "There are no hidden 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.domain_blocks": "Ùn c'hè manc'un duminiu bluccatu avà.",
+  "empty_column.favourited_statuses": "Ùn avete manc'unu statutu favuritu. Quandu aghjusterate unu à i vostri favuriti, sarà mustratu quì.",
+  "empty_column.favourites": "Nisunu hà aghjustatu stu statutu à i so favuriti. Quandu qualch'unu farà quessa, u so contu sarà mustratu quì.",
+  "empty_column.follow_requests": "Ùn avete manc'una dumanda d'abbunamentu. Quandu averete una, sarà mustrata quì.",
   "empty_column.hashtag": "Ùn c'hè ancu nunda quì.",
   "empty_column.home": "A vostr'accolta hè viota! Pudete andà nant'à {public} o pruvà a ricerca per truvà parsone da siguità.",
   "empty_column.home.public_timeline": "a linea pubblica",
   "empty_column.list": "Ùn c'hè ancu nunda quì. Quandu membri di sta lista manderanu novi statuti, i vidarete quì.",
-  "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.lists": "Ùn avete manc'una lista. Quandu farete una, sarà mustrata quì.",
+  "empty_column.mutes": "Per avà ùn avete manc'un utilizatore piattatu.",
   "empty_column.notifications": "Ùn avete ancu nisuna nutificazione. Interact with others to start the conversation.",
   "empty_column.public": "Ùn c'hè nunda quì! Scrivete qualcosa in pubblicu o seguitate utilizatori d'altre istanze per empie a linea pubblica",
   "follow_request.authorize": "Auturizà",
@@ -137,32 +138,32 @@
   "home.column_settings.show_reblogs": "Vede e spartere",
   "home.column_settings.show_replies": "Vede e risposte",
   "keyboard_shortcuts.back": "rivultà",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "per apre una lista d'utilizatori bluccati",
   "keyboard_shortcuts.boost": "sparte",
   "keyboard_shortcuts.column": "fucalizà un statutu indè una colonna",
   "keyboard_shortcuts.compose": "fucalizà nant'à l'area di ridazzione",
   "keyboard_shortcuts.description": "Descrizzione",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "per apre una culonna di missaghji diretti",
   "keyboard_shortcuts.down": "falà indè a lista",
   "keyboard_shortcuts.enter": "apre u statutu",
   "keyboard_shortcuts.favourite": "aghjunghje à i favuriti",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "per apre a lista di i favuriti",
+  "keyboard_shortcuts.federated": "per apre a linea pubblica federata",
   "keyboard_shortcuts.heading": "Accorte cù a tastera",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "per apre a linea d'accolta",
   "keyboard_shortcuts.hotkey": "Accorta",
   "keyboard_shortcuts.legend": "vede a legenda",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "per apre a linea pubblica lucale",
   "keyboard_shortcuts.mention": "mintuvà l'autore",
-  "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.pinned": "to open pinned toots list",
+  "keyboard_shortcuts.muted": "per apre a lista di l'utilizatori piattati",
+  "keyboard_shortcuts.my_profile": "per apre u vostru prufile",
+  "keyboard_shortcuts.notifications": "per apre a culonna di nutificazione",
+  "keyboard_shortcuts.pinned": "per apre a lista di statuti puntarulati",
   "keyboard_shortcuts.profile": "per apre u prufile di l'autore",
   "keyboard_shortcuts.reply": "risponde",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "per apre a lista di dumande d'abbunamentu",
   "keyboard_shortcuts.search": "fucalizà nant'à l'area di circata",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
+  "keyboard_shortcuts.start": "per apre a culonna \"per principià\"",
   "keyboard_shortcuts.toggle_hidden": "vede/piattà u testu daretu à l'avertimentu CW",
   "keyboard_shortcuts.toot": "scrive un novu statutu",
   "keyboard_shortcuts.unfocus": "ùn fucalizà più l'area di testu",
@@ -297,7 +298,7 @@
   "status.reblog": "Sparte",
   "status.reblog_private": "Sparte à l'audienza uriginale",
   "status.reblogged_by": "{name} hà spartutu",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
+  "status.reblogs.empty": "Per avà nisunu hà spartutu u statutu. Quandu qualch'unu u sparterà, u so contu sarà mustratu quì.",
   "status.redraft": "Sguassà è riscrive",
   "status.reply": "Risponde",
   "status.replyAll": "Risponde à tutti",
@@ -319,7 +320,7 @@
   "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} parlanu",
   "ui.beforeunload": "A bruttacopia sarà persa s'ellu hè chjosu Mastodon.",
   "upload_area.title": "Drag & drop per caricà un fugliale",
-  "upload_button.label": "Aghjunghje un media (JPEG, PNG, GIF, WebM, MP4)",
+  "upload_button.label": "Aghjunghje un media (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_form.description": "Discrive per i malvistosi",
   "upload_form.focus": "Riquatrà",
   "upload_form.undo": "Sguassà",
diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json
index b0f76e834..46aa954e8 100644
--- a/app/javascript/mastodon/locales/cs.json
+++ b/app/javascript/mastodon/locales/cs.json
@@ -10,11 +10,12 @@
   "account.endorse": "Představit na profilu",
   "account.follow": "Sleduj",
   "account.followers": "Sledovatelé",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "Tohoto uživatele ještě nikdo nesleduje.",
   "account.follows": "Sleduje",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "Tento uživatel ještě nikoho nesleduje.",
   "account.follows_you": "Sleduje vás",
   "account.hide_reblogs": "Skrýt boosty od uživatele @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Média",
   "account.mention": "Zmínit uživatele @{name}",
   "account.moved_to": "{name} se přesunul/a na:",
@@ -108,19 +109,19 @@
   "emoji_button.search_results": "Výsledky hledání",
   "emoji_button.symbols": "Symboly",
   "emoji_button.travel": "Cestování a místa",
-  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.blocks": "Ještě jste nezablokoval/a žádného uživatele.",
   "empty_column.community": "Místní časová osa je prázdná. Napište něco veřejně a rozhýbejte to tu!",
   "empty_column.direct": "Ještě nemáte žádné přímé zprávy. Pokud nějakou pošlete nebo dostanete, zobrazí se zde.",
-  "empty_column.domain_blocks": "There are no hidden 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.domain_blocks": "Ještě zde nejsou žádné skryté domény.",
+  "empty_column.favourited_statuses": "Ještě nemáte žádné oblíbené tooty. Pokud si nějaký oblíbíte, zobrazí se zde.",
+  "empty_column.favourites": "Tento toot si ještě nikdo neoblíbil. Pokud to někdo udělá, zobrazí se zde.",
+  "empty_column.follow_requests": "Ještě nemáte žádné požadavky o sledování. Pokud nějaký obdržíte, zobrazí se zde.",
   "empty_column.hashtag": "Pod tímto hashtagem ještě nic není.",
   "empty_column.home": "Vaše domovská časová osa je prázdná! Začněte navštívením {public} nebo použijte hledání a seznamte se s dalšími uživateli.",
   "empty_column.home.public_timeline": "veřejné časové osy",
   "empty_column.list": "V tomto seznamu ještě nic není. Pokud budou členové tohoto seznamu psát nové příspěvky, objeví se zde.",
-  "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.lists": "Ještě nemáte žádný seznam. Pokud nějaký vytvoříte, zobrazí se zde.",
+  "empty_column.mutes": "Ještě neignorujete žádné uživatele.",
   "empty_column.notifications": "Ještě nemáte žádná oznámení. Začněte konverzaci komunikováním s ostatními.",
   "empty_column.public": "Tady nic není! Napište něco veřejně, nebo manuálně začněte sledovat uživatele z jiných instancí, aby tu něco přibylo",
   "follow_request.authorize": "Autorizovat",
@@ -137,33 +138,33 @@
   "home.column_settings.show_reblogs": "Zobrazit boosty",
   "home.column_settings.show_replies": "Zobrazit odpovědi",
   "keyboard_shortcuts.back": "k návratu zpět",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "k otevření seznamu blokovaných uživatelů",
   "keyboard_shortcuts.boost": "k boostnutí",
   "keyboard_shortcuts.column": "k zaměření na příspěvek v jednom ze sloupců",
   "keyboard_shortcuts.compose": "k zaměření na psací prostor",
   "keyboard_shortcuts.description": "Popis",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "k otevření sloupce s přímými zprávami",
   "keyboard_shortcuts.down": "k přesunutí dolů v seznamu",
   "keyboard_shortcuts.enter": "k otevření příspěvku",
   "keyboard_shortcuts.favourite": "k oblíbení",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "k otevření seznamu oblíbených",
+  "keyboard_shortcuts.federated": "k otevření federované časové osy",
   "keyboard_shortcuts.heading": "Klávesové zkratky",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "k otevření domovské časové osy",
   "keyboard_shortcuts.hotkey": "Horká klávesa",
   "keyboard_shortcuts.legend": "k zobrazení této legendy",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "k otevření místní časové osy",
   "keyboard_shortcuts.mention": "ke zmínění autora",
-  "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.pinned": "to open pinned toots list",
+  "keyboard_shortcuts.muted": "k otevření seznamu ignorovaných uživatelů",
+  "keyboard_shortcuts.my_profile": "k otevření vašeho profilu",
+  "keyboard_shortcuts.notifications": "k otevření sloupce s oznámeními",
+  "keyboard_shortcuts.pinned": "k otevření seznamu připnutých tootů",
   "keyboard_shortcuts.profile": "k otevření autorova profilu",
   "keyboard_shortcuts.reply": "k odpovězení",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "k otevření seznamu požadavků o sledování",
   "keyboard_shortcuts.search": "k zaměření na vyhledávání",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
-  "keyboard_shortcuts.toggle_hidden": "k zobrazení/skrytí textu za CW",
+  "keyboard_shortcuts.start": "k otevření sloupce \"začít\"",
+  "keyboard_shortcuts.toggle_hidden": "k zobrazení/skrytí textu za varováním o obsahu",
   "keyboard_shortcuts.toot": "k napsání úplně nového tootu",
   "keyboard_shortcuts.unfocus": "ke zrušení soustředění na psací prostor/hledání",
   "keyboard_shortcuts.up": "k posunutí nahoru v seznamu",
@@ -297,7 +298,7 @@
   "status.reblog": "Boostnout",
   "status.reblog_private": "Boostnout původnímu publiku",
   "status.reblogged_by": "{name} boostnul/a",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
+  "status.reblogs.empty": "Tento toot ještě nikdo neboostnul. Pokud to někdo udělá, zobrazí se zde.",
   "status.redraft": "Vymazat a přepsat",
   "status.reply": "Odpovědět",
   "status.replyAll": "Odpovědět na vlákno",
@@ -319,7 +320,7 @@
   "trends.count_by_accounts": "{count} {rawCount, plural, one {člověk} other {lidí}} diskutuje",
   "ui.beforeunload": "Váš koncept se ztratí, pokud Mastodon opustíte.",
   "upload_area.title": "Přetažením nahrajete",
-  "upload_button.label": "Přidat média (JPEG, PNG, GIF, WebM, MP4)",
+  "upload_button.label": "Přidat média (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_form.description": "Popis pro zrakově postižené",
   "upload_form.focus": "Vystřihnout",
   "upload_form.undo": "Smazat",
diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json
new file mode 100644
index 000000000..a2eea600a
--- /dev/null
+++ b/app/javascript/mastodon/locales/cy.json
@@ -0,0 +1,337 @@
+{
+  "account.badges.bot": "Bot",
+  "account.block": "Blociwch @{name}",
+  "account.block_domain": "Cuddiwch bopeth rhag {domain}",
+  "account.blocked": "Blociwyd",
+  "account.direct": "Neges breifat @{name}",
+  "account.disclaimer_full": "Gall y wybodaeth isod adlewyrchu darlun anghyflawn o broffil defnyddiwr.",
+  "account.domain_blocked": "Domain hidden",
+  "account.edit_profile": "Golygu proffil",
+  "account.endorse": "Feature on profile",
+  "account.follow": "Dilyn",
+  "account.followers": "Dilynwyr",
+  "account.followers.empty": "Nid oes neb yn dilyn y defnyddiwr hwn eto.",
+  "account.follows": "Yn dilyn",
+  "account.follows.empty": "Nid yw'r defnyddiwr hwn yn dilyn unrhyw un eto.",
+  "account.follows_you": "Yn eich dilyn chi",
+  "account.hide_reblogs": "Hide boosts from @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.media": "Cyfryngau",
+  "account.mention": "Crybwyll @{name}",
+  "account.moved_to": "Mae @{name} wedi symud i:",
+  "account.mute": "Mute @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
+  "account.muted": "Distewyd",
+  "account.posts": "Tŵtiau",
+  "account.posts_with_replies": "Toots and replies",
+  "account.report": "Adroddwch @{name}",
+  "account.requested": "Awaiting approval. Click to cancel follow request",
+  "account.share": "Rhannwch broffil @{name}",
+  "account.show_reblogs": "Show boosts from @{name}",
+  "account.unblock": "Dadflociwch @{name}",
+  "account.unblock_domain": "Dadguddiwch {domain}",
+  "account.unendorse": "Don't feature on profile",
+  "account.unfollow": "Daddilynwch",
+  "account.unmute": "Unmute @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
+  "account.view_full_profile": "View full profile",
+  "alert.unexpected.message": "Digwyddodd gwall annisgwyl.",
+  "alert.unexpected.title": "Wps!",
+  "boost_modal.combo": "You can press {combo} to skip this next time",
+  "bundle_column_error.body": "Aeth rhywbeth o'i le tra'n llwytho'r elfen hon.",
+  "bundle_column_error.retry": "Ceisiwch eto",
+  "bundle_column_error.title": "Gwall rhwydwaith",
+  "bundle_modal_error.close": "Close",
+  "bundle_modal_error.message": "Aeth rhywbeth o'i le tra'n llwytho'r elfen hon.",
+  "bundle_modal_error.retry": "Ceiswich eto",
+  "column.blocks": "Defnyddwyr a flociwyd",
+  "column.community": "Llinell amser lleol",
+  "column.direct": "Negeseuon preifat",
+  "column.domain_blocks": "Parthau cuddiedig",
+  "column.favourites": "Ffefrynnau",
+  "column.follow_requests": "Ceisiadau dilyn",
+  "column.home": "Hafan",
+  "column.lists": "Rhestrau",
+  "column.mutes": "Defnyddwyr a ddistewyd",
+  "column.notifications": "Hysbysiadau",
+  "column.pins": "Pinned toot",
+  "column.public": "Federated timeline",
+  "column_back_button.label": "Nôl",
+  "column_header.hide_settings": "Hide settings",
+  "column_header.moveLeft_settings": "Symudwch y golofn i'r chwith",
+  "column_header.moveRight_settings": "Symudwch y golofn i'r dde",
+  "column_header.pin": "Piniwch",
+  "column_header.show_settings": "Dangos gosodiadau",
+  "column_header.unpin": "Dadbiniwch",
+  "column_subheading.settings": "Gosodiadau",
+  "community.column_settings.media_only": "Cyfryngau yn unig",
+  "compose_form.direct_message_warning": "Mi fydd y tŵt hwn ond yn cael ei anfon at y defnyddwyr sy'n cael eu crybwyll.",
+  "compose_form.direct_message_warning_learn_more": "Dysgwch fwy",
+  "compose_form.hashtag_warning": "Ni fydd y tŵt hwn wedi ei restru o dan unrhyw hashnod gan ei fod heb ei restru. Dim ond tŵtiau cyhoeddus gellid chwilota amdanynt drwy hashnod.",
+  "compose_form.lock_disclaimer": "Nid yw eich cyfri wedi'i {locked}. Gall unrhyw un eich dilyn i weld eich POSTS dilynwyr-yn-unig.",
+  "compose_form.lock_disclaimer.lock": "wedi ei gloi",
+  "compose_form.placeholder": "Be syd ar eich meddwl?",
+  "compose_form.publish": "Tŵt",
+  "compose_form.publish_loud": "{publish}!",
+  "compose_form.sensitive.marked": "Media is marked as sensitive",
+  "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+  "compose_form.spoiler.marked": "Testun wedi ei guddio gan rybudd",
+  "compose_form.spoiler.unmarked": "Nid yw'r testun wedi ei guddio",
+  "compose_form.spoiler_placeholder": "Ysgrifenwch eich rhybudd yma",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.confirm": "Blociwch",
+  "confirmations.block.message": "Ydych chi'n sicr eich bod eisiau blocio {name}?",
+  "confirmations.delete.confirm": "Dileu",
+  "confirmations.delete.message": "Ydych chi'n sicr eich bod eisiau dileu y statws hwn?",
+  "confirmations.delete_list.confirm": "Dileu",
+  "confirmations.delete_list.message": "Ydych chi'n sicr eich bod eisiau dileu y rhestr hwn am byth?",
+  "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.mute.confirm": "Mute",
+  "confirmations.mute.message": "Ydych chi'n sicr eich bod am ddistewi {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.unfollow.confirm": "Dad-ddilynwch",
+  "confirmations.unfollow.message": "Ydych chi'n sicr eich bod am ddad-ddilyn {name}?",
+  "embed.instructions": "Embed this status on your website by copying the code below.",
+  "embed.preview": "Dyma sut olwg fydd arno:",
+  "emoji_button.activity": "Gweithgarwch",
+  "emoji_button.custom": "Custom",
+  "emoji_button.flags": "Baneri",
+  "emoji_button.food": "Bwyd a Diod",
+  "emoji_button.label": "Mewnosodwch emoji",
+  "emoji_button.nature": "Natur",
+  "emoji_button.not_found": "Dim emojos!! (╯°□°)╯︵ ┻━┻",
+  "emoji_button.objects": "Gwrthrychau",
+  "emoji_button.people": "Pobl",
+  "emoji_button.recent": "Defnyddir yn aml",
+  "emoji_button.search": "Chwilio...",
+  "emoji_button.search_results": "Canlyniadau chwilio",
+  "emoji_button.symbols": "Symbolau",
+  "emoji_button.travel": "Teithio & Llefydd",
+  "empty_column.blocks": "Nid ydych wedi blocio unrhyw ddefnyddwyr eto.",
+  "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
+  "empty_column.direct": "Nid oes gennych unrhyw negeseuon preifat eto. Pan y byddwch yn anfon neu derbyn un, mi fydd yn ymddangos yma.",
+  "empty_column.domain_blocks": "Nid oes yna unrhyw barthau cuddiedig eto.",
+  "empty_column.favourited_statuses": "Nid oes gennych unrhyw hoff dwtiau eto. Pan y byddwch yn hoffi un, mi fydd yn ymddangos yma.",
+  "empty_column.favourites": "Nid oes neb wedi hoffi'r tŵt yma eto. Pan bydd rhywun yn ei hoffi, mi fyddent yn ymddangos yma.",
+  "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.hashtag": "Nid oes dim ar yr hashnod hwn eto.",
+  "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 instances to fill it up",
+  "follow_request.authorize": "Caniatau",
+  "follow_request.reject": "Gwrthod",
+  "getting_started.developers": "Datblygwyr",
+  "getting_started.documentation": "Dogfennaeth",
+  "getting_started.find_friends": "Canfod ffrindiau o Twitter",
+  "getting_started.heading": "Dechrau",
+  "getting_started.invite": "Gwahoddwch bobl",
+  "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}.",
+  "getting_started.security": "Diogelwch",
+  "getting_started.terms": "Terms of service",
+  "home.column_settings.basic": "Syml",
+  "home.column_settings.show_reblogs": "Show boosts",
+  "home.column_settings.show_replies": "Dangoswch ymatebion",
+  "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": "Disgrifiad",
+  "keyboard_shortcuts.direct": "i agor colofn negeseuon preifat",
+  "keyboard_shortcuts.down": "i symud lawr yn y rhestr",
+  "keyboard_shortcuts.enter": "i agor statws",
+  "keyboard_shortcuts.favourite": "i hoffi",
+  "keyboard_shortcuts.favourites": "i agor rhestr hoffi",
+  "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": "i grybwyll yr awdur",
+  "keyboard_shortcuts.muted": "to open muted users list",
+  "keyboard_shortcuts.my_profile": "i agor eich proffil",
+  "keyboard_shortcuts.notifications": "i agor colofn hysbysiadau",
+  "keyboard_shortcuts.pinned": "to open pinned toots list",
+  "keyboard_shortcuts.profile": "i agor proffil yr awdur",
+  "keyboard_shortcuts.reply": "i ateb",
+  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.search": "to focus search",
+  "keyboard_shortcuts.start": "to open \"get started\" column",
+  "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW",
+  "keyboard_shortcuts.toot": "i ddechrau tŵt newydd sbon",
+  "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
+  "keyboard_shortcuts.up": "to move up in the list",
+  "lightbox.close": "Cau",
+  "lightbox.next": "Nesaf",
+  "lightbox.previous": "Previous",
+  "lists.account.add": "Ychwanegwch at restr",
+  "lists.account.remove": "Remove from list",
+  "lists.delete": "Dileu rhestr",
+  "lists.edit": "Golygwch restr",
+  "lists.new.create": "Ychwanegwch restr",
+  "lists.new.title_placeholder": "New list title",
+  "lists.search": "Search among people you follow",
+  "lists.subheading": "Eich rhestrau",
+  "loading_indicator.label": "Llwytho...",
+  "media_gallery.toggle_visible": "Toggle visibility",
+  "missing_indicator.label": "Heb ei ganfod",
+  "missing_indicator.sublabel": "Ni ellid canfod yr adnodd hwn",
+  "mute_modal.hide_notifications": "Cuddiwch hysbysiadau rhag y defnyddiwr hwn?",
+  "navigation_bar.apps": "Apiau symudol",
+  "navigation_bar.blocks": "Defnyddwyr wedi eu blocio",
+  "navigation_bar.community_timeline": "Local timeline",
+  "navigation_bar.compose": "Cyfansoddwch dŵt newydd",
+  "navigation_bar.direct": "Negeseuon preifat",
+  "navigation_bar.discover": "Darganfyddwch",
+  "navigation_bar.domain_blocks": "Parthau cuddiedig",
+  "navigation_bar.edit_profile": "Golygu proffil",
+  "navigation_bar.favourites": "Ffefrynnau",
+  "navigation_bar.filters": "Muted words",
+  "navigation_bar.follow_requests": "Ceisiadau dilyn",
+  "navigation_bar.info": "About this instance",
+  "navigation_bar.keyboard_shortcuts": "Hotkeys",
+  "navigation_bar.lists": "Rhestrau",
+  "navigation_bar.logout": "Allgofnodi",
+  "navigation_bar.mutes": "Muted users",
+  "navigation_bar.personal": "Personol",
+  "navigation_bar.pins": "Tŵtiau wedi eu pinio",
+  "navigation_bar.preferences": "Dewisiadau",
+  "navigation_bar.public_timeline": "Federated timeline",
+  "navigation_bar.security": "Diogelwch",
+  "notification.favourite": "hoffodd {name} eich statws",
+  "notification.follow": "dilynodd {name} chi",
+  "notification.mention": "{name} mentioned you",
+  "notification.reblog": "{name} boosted your status",
+  "notifications.clear": "Clirio hysbysiadau",
+  "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
+  "notifications.column_settings.alert": "Desktop notifications",
+  "notifications.column_settings.favourite": "Ffefrynnau:",
+  "notifications.column_settings.follow": "Dilynwyr newydd:",
+  "notifications.column_settings.mention": "Mentions:",
+  "notifications.column_settings.push": "Push notifications",
+  "notifications.column_settings.push_meta": "This device",
+  "notifications.column_settings.reblog": "Boosts:",
+  "notifications.column_settings.show": "Show in column",
+  "notifications.column_settings.sound": "Chwarae sain",
+  "notifications.group": "{count} o hysbysiadau",
+  "onboarding.done": "Done",
+  "onboarding.next": "Nesaf",
+  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.full_handle": "Your full handle",
+  "onboarding.page_one.handle_hint": "This is what you would tell your friends to search for.",
+  "onboarding.page_one.welcome": "Croeso i Mastodon!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+  "onboarding.page_six.almost_done": "Almost done...",
+  "onboarding.page_six.appetoot": "Bon Apetŵt!",
+  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "canllawiau cymunedol",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+  "onboarding.page_six.various_app": "apiau symudol",
+  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.skip": "Skip",
+  "privacy.change": "Adjust status privacy",
+  "privacy.direct.long": "Post to mentioned users only",
+  "privacy.direct.short": "Direct",
+  "privacy.private.long": "Post to followers only",
+  "privacy.private.short": "Dilynwyr-yn-unig",
+  "privacy.public.long": "Post to public timelines",
+  "privacy.public.short": "Cyhoeddus",
+  "privacy.unlisted.long": "Do not show in public timelines",
+  "privacy.unlisted.short": "Heb ei restru",
+  "regeneration_indicator.label": "Llwytho…",
+  "regeneration_indicator.sublabel": "Mae eich ffrwd cartref yn cael ei baratoi!",
+  "relative_time.days": "{number}d",
+  "relative_time.hours": "{number}h",
+  "relative_time.just_now": "nawr",
+  "relative_time.minutes": "{number}m",
+  "relative_time.seconds": "{number}s",
+  "reply_indicator.cancel": "Canslo",
+  "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 instance moderators. You can provide an explanation of why you are reporting this account below:",
+  "report.placeholder": "Sylwadau ychwanegol",
+  "report.submit": "Submit",
+  "report.target": "Report {target}",
+  "search.placeholder": "Chwilio",
+  "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": "hashnod",
+  "search_popout.tips.status": "statws",
+  "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
+  "search_popout.tips.user": "defnyddiwr",
+  "search_results.accounts": "Pobl",
+  "search_results.hashtags": "Hanshnodau",
+  "search_results.statuses": "Twtiau",
+  "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
+  "standalone.public_title": "A look inside...",
+  "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
+  "status.cannot_reblog": "This post cannot be boosted",
+  "status.delete": "Dileu",
+  "status.detailed_status": "Detailed conversation view",
+  "status.direct": "Neges breifat @{name}",
+  "status.embed": "Plannu",
+  "status.favourite": "Favourite",
+  "status.filtered": "Filtered",
+  "status.load_more": "Llwythwch mwy",
+  "status.media_hidden": "Media hidden",
+  "status.mention": "Mention @{name}",
+  "status.more": "Mwy",
+  "status.mute": "Mute @{name}",
+  "status.mute_conversation": "Mute conversation",
+  "status.open": "Expand this status",
+  "status.pin": "Pin on profile",
+  "status.pinned": "Pinned toot",
+  "status.reblog": "Boost",
+  "status.reblog_private": "Boost to original audience",
+  "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.reply": "Ateb",
+  "status.replyAll": "Ateb i edefyn",
+  "status.report": "Report @{name}",
+  "status.sensitive_toggle": "Click to view",
+  "status.sensitive_warning": "Cynnwys sensitif",
+  "status.share": "Rhannwch",
+  "status.show_less": "Dangoswch lai",
+  "status.show_less_all": "Dangoswch lai i bawb",
+  "status.show_more": "Dangoswch fwy",
+  "status.show_more_all": "Show more for all",
+  "status.unmute_conversation": "Unmute conversation",
+  "status.unpin": "Unpin from profile",
+  "tabs_bar.federated_timeline": "Federated",
+  "tabs_bar.home": "Hafan",
+  "tabs_bar.local_timeline": "Lleol",
+  "tabs_bar.notifications": "Hysbysiadau",
+  "tabs_bar.search": "Chwilio",
+  "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
+  "upload_area.title": "Drag & drop to upload",
+  "upload_button.label": "Add media (JPEG, PNG, GIF, WebM, MP4, MOV)",
+  "upload_form.description": "Describe for the visually impaired",
+  "upload_form.focus": "Crop",
+  "upload_form.undo": "Delete",
+  "upload_progress.label": "Uwchlwytho...",
+  "video.close": "Close video",
+  "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": "Chwarae",
+  "video.unmute": "Unmute sound"
+}
diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json
index 83f049a5b..959a50377 100644
--- a/app/javascript/mastodon/locales/da.json
+++ b/app/javascript/mastodon/locales/da.json
@@ -10,11 +10,12 @@
   "account.endorse": "Fremhæv på profil",
   "account.follow": "Følg",
   "account.followers": "Følgere",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "Der er endnu ingen der følger denne bruger.",
   "account.follows": "Følger",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "Denne bruger følger endnu ikke nogen.",
   "account.follows_you": "Følger dig",
   "account.hide_reblogs": "Skjul fremhævelserne fra @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Medie",
   "account.mention": "Nævn @{name}",
   "account.moved_to": "{name} er flyttet til:",
@@ -108,19 +109,19 @@
   "emoji_button.search_results": "Søgeresultater",
   "emoji_button.symbols": "Symboler",
   "emoji_button.travel": "Rejser & steder",
-  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.blocks": "Du har ikke blokeret nogen endnu.",
   "empty_column.community": "Den lokale tidslinje er tom. Skriv noget offentligt for at starte lavinen!",
   "empty_column.direct": "Du har endnu ingen direkte beskeder. Når du sender eller modtager en, vil den vises her.",
-  "empty_column.domain_blocks": "There are no hidden 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.domain_blocks": "Der er endnu ikke nogle skjulte domæner.",
+  "empty_column.favourited_statuses": "Du har endnu ikke favoriseret nogen trut. Når du favoriserer et, vil det blive vist her.",
+  "empty_column.favourites": "Endnu ingen har favoriseret dette trut. Når en anden gør vil det blive vist her.",
+  "empty_column.follow_requests": "Du har endnu ingen følgeranmodninger. Når du modtager en, vil den komme frem her.",
   "empty_column.hashtag": "Dette hashtag indeholder endnu ikke noget.",
   "empty_column.home": "Din hjemme tidslinje er tom! Besøg {public} eller brug søgningen for at komme igang og møde andre brugere.",
   "empty_column.home.public_timeline": "den offentlige tidslinje",
   "empty_column.list": "Der er endnu intet i denne liste. Når medlemmer af denne liste poster nye statusser, vil de vises her.",
-  "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.lists": "Du har endnu ingen lister. Når du opretter en, vil den blive vist her.",
+  "empty_column.mutes": "Du har endnu ikke dæmpet nogen som helst bruger.",
   "empty_column.notifications": "Du har endnu ingen notifikationer. Tag ud og bland dig med folkemængden for at starte samtalen.",
   "empty_column.public": "Der er ikke noget at se her! Skriv noget offentligt eller start ud med manuelt at følge brugere fra andre instanser for st udfylde tomrummet",
   "follow_request.authorize": "Godkend",
@@ -137,32 +138,32 @@
   "home.column_settings.show_reblogs": "Vis fremhævelser",
   "home.column_settings.show_replies": "Vis svar",
   "keyboard_shortcuts.back": "for at navigere dig tilbage",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "for at åbne listen over blokerede brugere",
   "keyboard_shortcuts.boost": "for at fremhæve",
   "keyboard_shortcuts.column": "for at fokusere på en status i en af kolonnerne",
   "keyboard_shortcuts.compose": "for at fokusere på skriveområdet",
   "keyboard_shortcuts.description": "Beskrivelse",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "for at åbne privat besked kolonnen",
   "keyboard_shortcuts.down": "for at rykke ned ad listen",
   "keyboard_shortcuts.enter": "for at åbne status",
   "keyboard_shortcuts.favourite": "for at favorisere",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "for at åbne listen over favoritter",
+  "keyboard_shortcuts.federated": "for at åbne den forenede tidslinje",
   "keyboard_shortcuts.heading": "Tastaturgenveje",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "for at åbne hjem tidslinjen",
   "keyboard_shortcuts.hotkey": "Hurtigtast",
   "keyboard_shortcuts.legend": "for at vise denne legende",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "for at åbne den lokale tidslinje",
   "keyboard_shortcuts.mention": "for at nævne forfatteren",
-  "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.pinned": "to open pinned toots list",
+  "keyboard_shortcuts.muted": "for at åbne listen over dæmpede brugere",
+  "keyboard_shortcuts.my_profile": "for at åbne din profil",
+  "keyboard_shortcuts.notifications": "for at åbne notifikations kolonnen",
+  "keyboard_shortcuts.pinned": "for at åbne listen over fastgjorte trut",
   "keyboard_shortcuts.profile": "til profil af åben forfatter",
   "keyboard_shortcuts.reply": "for at svare",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "for at åbne listen over følgeranmodninger",
   "keyboard_shortcuts.search": "for at fokusere søgningen",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
+  "keyboard_shortcuts.start": "for at åbne \"kom igen\" kolonnen",
   "keyboard_shortcuts.toggle_hidden": "for at vise/skjule tekst bag CW",
   "keyboard_shortcuts.toot": "for at påbegynde et helt nyt trut",
   "keyboard_shortcuts.unfocus": "for at fjerne fokus fra skriveområde/søgning",
@@ -186,7 +187,7 @@
   "navigation_bar.apps": "Mobil apps",
   "navigation_bar.blocks": "Blokerede brugere",
   "navigation_bar.community_timeline": "Lokal tidslinje",
-  "navigation_bar.compose": "Compose new toot",
+  "navigation_bar.compose": "Skriv nyt trut",
   "navigation_bar.direct": "Direkte beskeder",
   "navigation_bar.discover": "Opdag",
   "navigation_bar.domain_blocks": "Skjulte domæner",
@@ -200,8 +201,8 @@
   "navigation_bar.logout": "Logud",
   "navigation_bar.mutes": "Dæmpede brugere",
   "navigation_bar.personal": "Personligt",
-  "navigation_bar.pins": "Fastgjorte toots",
-  "navigation_bar.preferences": "Indstillinger",
+  "navigation_bar.pins": "Fastgjorte trut",
+  "navigation_bar.preferences": "Præferencer",
   "navigation_bar.public_timeline": "Fælles tidslinje",
   "navigation_bar.security": "Sikkerhed",
   "notification.favourite": "{name} favoriserede din status",
@@ -237,7 +238,7 @@
   "onboarding.page_six.guidelines": "retningslinjer for fællesskabet",
   "onboarding.page_six.read_guidelines": "Læs venligst {domain}s {guidelines}!",
   "onboarding.page_six.various_app": "apps til mobilen",
-  "onboarding.page_three.profile": "Rediger din profil for at ændre profilbillede, beskrivelse og visningsnavn. Der vil du også finde andre indstillinger.",
+  "onboarding.page_three.profile": "Rediger din profil for at ændre profilbillede, beskrivelse og visningsnavn. Der vil du også finde andre præferencer.",
   "onboarding.page_three.search": "Brug søgefeltdet for at finde folk og at kigge på hashtags, så som {illustration} and {introductions}. For at finde en person der ikke er på denne instans, brug deres fulde brugernavn.",
   "onboarding.page_two.compose": "Skriv opslag fra skrive kolonnen. Du kan uploade billeder, ændre privatlivsindstillinger, og tilføje indholds advarsler med ikoner forneden.",
   "onboarding.skip": "Spring over",
@@ -273,14 +274,14 @@
   "search_popout.tips.user": "bruger",
   "search_results.accounts": "Folk",
   "search_results.hashtags": "Emnetags",
-  "search_results.statuses": "Toote",
+  "search_results.statuses": "Trut",
   "search_results.total": "{count, number} {count, plural, et {result} andre {results}}",
   "standalone.public_title": "Et kig indenfor...",
   "status.block": "Bloker @{name}",
   "status.cancel_reblog_private": "Fremhæv ikke længere",
   "status.cannot_reblog": "Denne post kan ikke fremhæves",
   "status.delete": "Slet",
-  "status.detailed_status": "Detailed conversation view",
+  "status.detailed_status": "Detaljeret visning af samtale",
   "status.direct": "Send direkte besked til @{name}",
   "status.embed": "Indlejre",
   "status.favourite": "Favorit",
@@ -297,7 +298,7 @@
   "status.reblog": "Fremhæv",
   "status.reblog_private": "Fremhæv til oprindeligt publikum",
   "status.reblogged_by": "{name} fremhævede",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
+  "status.reblogs.empty": "Der er endnu ingen der har fremhævet dette trut. Når der er nogen der gør, vil det blive vist her.",
   "status.redraft": "Slet og omskriv",
   "status.reply": "Svar",
   "status.replyAll": "Svar samtale",
@@ -319,7 +320,7 @@
   "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} snakker",
   "ui.beforeunload": "Din kladde vil gå tabt hvis du forlader Mastodon.",
   "upload_area.title": "Træk og slip for at uploade",
-  "upload_button.label": "Tilføj medie (JPEG, PNG, GIF, WebM, MP4)",
+  "upload_button.label": "Tilføj medie (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_form.description": "Beskriv for de svagtseende",
   "upload_form.focus": "Beskær",
   "upload_form.undo": "Slet",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index e117175cd..4f1b2c8a0 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -7,14 +7,15 @@
   "account.disclaimer_full": "Das Profil wird möglicherweise unvollständig wiedergegeben.",
   "account.domain_blocked": "Domain versteckt",
   "account.edit_profile": "Profil bearbeiten",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "Auf Profil hervorheben",
   "account.follow": "Folgen",
   "account.followers": "Folgende",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "Diesem Profil folgt noch niemand.",
   "account.follows": "Folgt",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "Dieses Profil folgt noch niemandem.",
   "account.follows_you": "Folgt dir",
   "account.hide_reblogs": "Geteilte Beiträge von @{name} verbergen",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Medien",
   "account.mention": "@{name} erwähnen",
   "account.moved_to": "{name} ist umgezogen auf:",
@@ -29,7 +30,7 @@
   "account.show_reblogs": "Von @{name} geteilte Beiträge anzeigen",
   "account.unblock": "@{name} entblocken",
   "account.unblock_domain": "{domain} wieder anzeigen",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "Nicht auf Profil hervorheben",
   "account.unfollow": "Entfolgen",
   "account.unmute": "@{name} nicht mehr stummschalten",
   "account.unmute_notifications": "Benachrichtigungen von @{name} einschalten",
@@ -69,7 +70,7 @@
   "compose_form.hashtag_warning": "Dieser Beitrag wird nicht unter einen dieser Hashtags sichtbar sein, solange er ungelistet ist. Bei einer Suche kann er nicht gefunden werden.",
   "compose_form.lock_disclaimer": "Dein Profil ist nicht {locked}. Wer dir folgen will, kann das jederzeit tun und dann auch deine privaten Beiträge sehen.",
   "compose_form.lock_disclaimer.lock": "gesperrt",
-  "compose_form.placeholder": "Was gibt's neues?",
+  "compose_form.placeholder": "Was gibt's Neues?",
   "compose_form.publish": "Tröt",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.marked": "Medien sind als heikel markiert",
@@ -89,7 +90,7 @@
   "confirmations.mute.confirm": "Stummschalten",
   "confirmations.mute.message": "Bist du dir sicher, dass du {name} stummschalten möchtest?",
   "confirmations.redraft.confirm": "Löschen und neu erstellen",
-  "confirmations.redraft.message": "Bist du dir sicher, dass du diesen Status löschen und neu machen möchtest? Du wirst alle Antworten, Boosts und Favoriten darauf verlieren.",
+  "confirmations.redraft.message": "Bist du dir sicher, dass du diesen Status löschen und neu machen möchtest? Favoriten und Boosts werden verloren gehen und Antworten zu diesem Post werden verwaist sein.",
   "confirmations.unfollow.confirm": "Entfolgen",
   "confirmations.unfollow.message": "Bist du dir sicher, dass du {name} entfolgen möchtest?",
   "embed.instructions": "Du kannst diesen Beitrag auf deiner Webseite einbetten, indem du den folgenden Code einfügst.",
@@ -108,19 +109,19 @@
   "emoji_button.search_results": "Suchergebnisse",
   "emoji_button.symbols": "Symbole",
   "emoji_button.travel": "Reisen und Orte",
-  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.blocks": "Du hast keine Profile blockiert.",
   "empty_column.community": "Die lokale Zeitleiste ist leer. Schreibe einen öffentlichen Beitrag, um den Ball ins Rollen zu bringen!",
   "empty_column.direct": "Du hast noch keine Direktnachrichten erhalten. Wenn du eine sendest oder empfängst, wird sie hier zu sehen sein.",
-  "empty_column.domain_blocks": "There are no hidden 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.domain_blocks": "Es ist noch keine versteckten Domains.",
+  "empty_column.favourited_statuses": "Du hast noch keine favorisierten Tröts. Wenn du einen favorisierst, wird er hier erscheinen.",
+  "empty_column.favourites": "Noch niemand hat diesen Beitrag favorisiert. Sobald es jemand tut, wird das hier angezeigt.",
+  "empty_column.follow_requests": "Du hast noch keine Folge-Anfragen. Sobald du eine erhältst, wird sie hier angezeigt.",
   "empty_column.hashtag": "Unter diesem Hashtag gibt es noch nichts.",
   "empty_column.home": "Deine Startseite ist leer! Besuche {public} oder nutze die Suche, um loszulegen und andere Leute zu finden.",
   "empty_column.home.public_timeline": "die öffentliche Zeitleiste",
   "empty_column.list": "Diese Liste ist derzeit leer. Wenn Wesen auf dieser Liste neue Beiträge veröffentlichen werden sie hier erscheinen.",
-  "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.lists": "Du hast noch keine Listen. Wenn du eine anlegst, wird sie hier angezeigt.",
+  "empty_column.mutes": "Du hast keine Profile stummgeschaltet.",
   "empty_column.notifications": "Du hast noch keine Mitteilungen. Interagiere mit anderen, um ins Gespräch zu kommen.",
   "empty_column.public": "Hier ist nichts zu sehen! Schreibe etwas öffentlich oder folge Profilen von anderen Instanzen, um die Zeitleiste aufzufüllen",
   "follow_request.authorize": "Erlauben",
@@ -137,35 +138,35 @@
   "home.column_settings.show_reblogs": "Geteilte Beiträge anzeigen",
   "home.column_settings.show_replies": "Antworten anzeigen",
   "keyboard_shortcuts.back": "zurück navigieren",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "Liste blockierter Profile öffnen",
   "keyboard_shortcuts.boost": "boosten",
   "keyboard_shortcuts.column": "einen Status in einer der Spalten fokussieren",
-  "keyboard_shortcuts.compose": "um das Textfeld zu fokussieren",
+  "keyboard_shortcuts.compose": "fokussiere das Tröt-Eingabefeld",
   "keyboard_shortcuts.description": "Beschreibung",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "Direct-Message-Spalte öffnen",
   "keyboard_shortcuts.down": "sich in der Liste hinunter bewegen",
-  "keyboard_shortcuts.enter": "um den Status zu öffnen",
+  "keyboard_shortcuts.enter": "Status öffnen",
   "keyboard_shortcuts.favourite": "um zu favorisieren",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "Favoriten-Liste öffnen",
+  "keyboard_shortcuts.federated": "Förderierte Zeitleiste öffnen",
   "keyboard_shortcuts.heading": "Tastenkombinationen",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "Startseite öffnen",
   "keyboard_shortcuts.hotkey": "Tastenkürzel",
-  "keyboard_shortcuts.legend": "um diese Übersicht anzuzeigen",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.legend": "diese Übersicht anzeigen",
+  "keyboard_shortcuts.local": "Lokale Zeitleiste öffnen",
   "keyboard_shortcuts.mention": "um Autor_in zu erwähnen",
-  "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.pinned": "to open pinned toots list",
-  "keyboard_shortcuts.profile": "um Profil des Autors zu öffnen",
-  "keyboard_shortcuts.reply": "um zu antworten",
-  "keyboard_shortcuts.requests": "to open follow requests list",
-  "keyboard_shortcuts.search": "um die Suche zu fokussieren",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
-  "keyboard_shortcuts.toggle_hidden": "um den Text hinter einer Inhaltswarnung zu verstecken oder ihn anzuzeigen",
-  "keyboard_shortcuts.toot": "um einen neuen Toot zu beginnen",
-  "keyboard_shortcuts.unfocus": "um das Textfeld/die Suche nicht mehr zu fokussieren",
+  "keyboard_shortcuts.muted": "Liste stummgeschalteter Profile öffnen",
+  "keyboard_shortcuts.my_profile": "Dein Profil öffnen",
+  "keyboard_shortcuts.notifications": "Benachrichtigungsspalte öffnen",
+  "keyboard_shortcuts.pinned": "Liste angehefteter Tröts öffnen",
+  "keyboard_shortcuts.profile": "Profil des Autors öffnen",
+  "keyboard_shortcuts.reply": "antworten",
+  "keyboard_shortcuts.requests": "Liste der Folge-Anfragen  öffnen",
+  "keyboard_shortcuts.search": "Suche fokussieren",
+  "keyboard_shortcuts.start": "\"Erste Schritte-Spalte öffnen",
+  "keyboard_shortcuts.toggle_hidden": "Text hinter einer Inhaltswarnung verstecken/anzeigen",
+  "keyboard_shortcuts.toot": "einen neuen Toot beginnen",
+  "keyboard_shortcuts.unfocus": "Textfeld/die Suche nicht mehr fokussieren",
   "keyboard_shortcuts.up": "sich in der Liste hinauf bewegen",
   "lightbox.close": "Schließen",
   "lightbox.next": "Weiter",
@@ -183,10 +184,10 @@
   "missing_indicator.label": "Nicht gefunden",
   "missing_indicator.sublabel": "Die Ressource konnte nicht gefunden werden",
   "mute_modal.hide_notifications": "Benachrichtigungen von diesem Account verbergen?",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "Mobile Apps",
   "navigation_bar.blocks": "Blockierte Profile",
   "navigation_bar.community_timeline": "Lokale Zeitleiste",
-  "navigation_bar.compose": "Compose new toot",
+  "navigation_bar.compose": "Neuen Beitrag verfassen",
   "navigation_bar.direct": "Direktnachrichten",
   "navigation_bar.discover": "Entdecken",
   "navigation_bar.domain_blocks": "Versteckte Domains",
@@ -297,7 +298,7 @@
   "status.reblog": "Teilen",
   "status.reblog_private": "An das eigentliche Publikum teilen",
   "status.reblogged_by": "{name} teilte",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
+  "status.reblogs.empty": "Diesen Beitrag hat noch niemand geboostet. Sobald es jemand tun, wird er hier angezeigt.",
   "status.redraft": "Löschen und neu erstellen",
   "status.reply": "Antworten",
   "status.replyAll": "Auf Thread antworten",
@@ -319,7 +320,7 @@
   "trends.count_by_accounts": "{count} {rawCount, plural, eine {Person} other {Personen}} reden darüber",
   "ui.beforeunload": "Dein Entwurf geht verloren, wenn du Mastodon verlässt.",
   "upload_area.title": "Zum Hochladen hereinziehen",
-  "upload_button.label": "Mediendatei hinzufügen",
+  "upload_button.label": "Mediendatei hinzufügen (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_form.description": "Für Menschen mit Sehbehinderung beschreiben",
   "upload_form.focus": "Zuschneiden",
   "upload_form.undo": "Löschen",
diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json
index 0c55090f6..5215bfb4e 100644
--- a/app/javascript/mastodon/locales/defaultMessages.json
+++ b/app/javascript/mastodon/locales/defaultMessages.json
@@ -611,6 +611,10 @@
         "id": "account.edit_profile"
       },
       {
+        "defaultMessage": "Ownership of this link was checked on {date}",
+        "id": "account.link_verified_on"
+      },
+      {
         "defaultMessage": "Follows you",
         "id": "account.follows_you"
       },
@@ -1837,6 +1841,15 @@
   {
     "descriptors": [
       {
+        "defaultMessage": "Toot",
+        "id": "compose_form.publish"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/ui/components/columns_area.json"
+  },
+  {
+    "descriptors": [
+      {
         "defaultMessage": "Cancel",
         "id": "confirmation_modal.cancel"
       }
@@ -2128,4 +2141,4 @@
     ],
     "path": "app/javascript/mastodon/features/video/index.json"
   }
-]
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json
index 0ab2d4bac..9c2a31722 100644
--- a/app/javascript/mastodon/locales/el.json
+++ b/app/javascript/mastodon/locales/el.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "Σε ακολουθεί",
   "account.hide_reblogs": "Απόκρυψη προωθήσεων από @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Πολυμέσα",
   "account.mention": "Ανάφερε @{name}",
   "account.moved_to": "{name} μεταφέρθηκε στο:",
@@ -319,7 +320,7 @@
   "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} μιλάνε",
   "ui.beforeunload": "Το προσχέδιό σου θα χαθεί αν φύγεις από το Mastodon.",
   "upload_area.title": "Drag & drop για να ανεβάσεις",
-  "upload_button.label": "Πρόσθεσε πολυμέσα (JPEG, PNG, GIF, WebM, MP4)",
+  "upload_button.label": "Πρόσθεσε πολυμέσα (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_form.description": "Περιέγραψε για όσους & όσες έχουν προβλήματα όρασης",
   "upload_form.focus": "Περικοπή",
   "upload_form.undo": "Διαγραφή",
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index def1e0a56..b117dad95 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "Follows you",
   "account.hide_reblogs": "Hide boosts from @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Media",
   "account.mention": "Mention @{name}",
   "account.moved_to": "{name} has moved to:",
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index ab584840e..dcc565eb9 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "Sekvas vin",
   "account.hide_reblogs": "Kaŝi diskonigojn de @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Aŭdovidaĵoj",
   "account.mention": "Mencii @{name}",
   "account.moved_to": "{name} moviĝis al:",
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
index 07e110048..355c901fa 100644
--- a/app/javascript/mastodon/locales/es.json
+++ b/app/javascript/mastodon/locales/es.json
@@ -7,14 +7,15 @@
   "account.disclaimer_full": "La siguiente información del usuario puede estar incompleta.",
   "account.domain_blocked": "Dominio oculto",
   "account.edit_profile": "Editar perfil",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "Mostrar en perfil",
   "account.follow": "Seguir",
   "account.followers": "Seguidores",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "Nadie sigue a este usuario todavía.",
   "account.follows": "Sigue",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "Este usuario todavía no sigue a nadie.",
   "account.follows_you": "Te sigue",
   "account.hide_reblogs": "Ocultar retoots de @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Media",
   "account.mention": "Mencionar a @{name}",
   "account.moved_to": "{name} se ha mudado a:",
@@ -29,12 +30,12 @@
   "account.show_reblogs": "Mostrar retoots de @{name}",
   "account.unblock": "Desbloquear a @{name}",
   "account.unblock_domain": "Mostrar a {domain}",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "No mostrar en el perfil",
   "account.unfollow": "Dejar de seguir",
   "account.unmute": "Dejar de silenciar a @{name}",
   "account.unmute_notifications": "Dejar de silenciar las notificaciones de @{name}",
   "account.view_full_profile": "Ver perfil completo",
-  "alert.unexpected.message": "An unexpected error occurred.",
+  "alert.unexpected.message": "Hubo un error inesperado.",
   "alert.unexpected.title": "Oops!",
   "boost_modal.combo": "Puedes presionar {combo} para saltear este aviso la próxima vez",
   "bundle_column_error.body": "Algo salió mal al cargar este componente.",
@@ -45,8 +46,8 @@
   "bundle_modal_error.retry": "Inténtalo de nuevo",
   "column.blocks": "Usuarios bloqueados",
   "column.community": "Línea de tiempo local",
-  "column.direct": "Direct messages",
-  "column.domain_blocks": "Hidden domains",
+  "column.direct": "Mensajes directos",
+  "column.domain_blocks": "Dominios ocultos",
   "column.favourites": "Favoritos",
   "column.follow_requests": "Solicitudes de seguimiento",
   "column.home": "Inicio",
@@ -63,9 +64,9 @@
   "column_header.show_settings": "Mostrar ajustes",
   "column_header.unpin": "Dejar de fijar",
   "column_subheading.settings": "Ajustes",
-  "community.column_settings.media_only": "Media Only",
-  "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.",
-  "compose_form.direct_message_warning_learn_more": "Learn more",
+  "community.column_settings.media_only": "Solo media",
+  "compose_form.direct_message_warning": "Este toot solo será enviado a los usuarios mencionados.",
+  "compose_form.direct_message_warning_learn_more": "Aprender mas",
   "compose_form.hashtag_warning": "Este toot no se mostrará bajo hashtags porque no es público. Sólo los toots públicos se pueden buscar por hashtag.",
   "compose_form.lock_disclaimer": "Tu cuenta no está bloqueada. Todos pueden seguirte para ver tus toots solo para seguidores.",
   "compose_form.lock_disclaimer.lock": "bloqueado",
@@ -85,11 +86,11 @@
   "confirmations.delete_list.confirm": "Delete",
   "confirmations.delete_list.message": "¿Seguro que quieres borrar esta lista permanentemente?",
   "confirmations.domain_block.confirm": "Ocultar dominio entero",
-  "confirmations.domain_block.message": "¿Seguro de que quieres bloquear al dominio entero? En algunos casos es preferible bloquear o silenciar objetivos determinados.",
+  "confirmations.domain_block.message": "¿Seguro de que quieres bloquear al dominio {domain} entero? En general unos cuantos bloqueos y silenciados concretos es suficiente y preferible.",
   "confirmations.mute.confirm": "Silenciar",
   "confirmations.mute.message": "¿Estás seguro de que quieres silenciar a {name}?",
-  "confirmations.redraft.confirm": "Delete & redraft",
-  "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.",
+  "confirmations.redraft.confirm": "Borrar y volver a borrador",
+  "confirmations.redraft.message": "Estás seguro de que quieres borrar este estado y volverlo a borrador? Perderás todas las respuestas, impulsos y favoritos asociados a él, y las respuestas a la publicación original quedarán huérfanos.",
   "confirmations.unfollow.confirm": "Dejar de seguir",
   "confirmations.unfollow.message": "¿Estás seguro de que quieres dejar de seguir a {name}?",
   "embed.instructions": "Añade este toot a tu sitio web con el siguiente código.",
@@ -108,62 +109,62 @@
   "emoji_button.search_results": "Resultados de búsqueda",
   "emoji_button.symbols": "Símbolos",
   "emoji_button.travel": "Viajes y lugares",
-  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.blocks": "Aún no has bloqueado a ningún usuario.",
   "empty_column.community": "La línea de tiempo local está vacía. ¡Escribe algo para empezar la fiesta!",
-  "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 hidden 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.direct": "Aún no tienes ningún mensaje directo. Cuando envíes o recibas uno, se mostrará aquí.",
+  "empty_column.domain_blocks": "Todavía no hay dominios ocultos.",
+  "empty_column.favourited_statuses": "Aún no tienes toots preferidos. Cuando marques uno como favorito, aparecerá aquí.",
+  "empty_column.favourites": "Nadie ha marcado este toot como preferido. Cuando alguien lo haga, aparecerá aquí.",
+  "empty_column.follow_requests": "No tienes ninguna petición de seguidor. Cuando recibas una, se mostrará aquí.",
   "empty_column.hashtag": "No hay nada en este hashtag aún.",
   "empty_column.home": "No estás siguiendo a nadie aún. Visita {public} o haz búsquedas para empezar y conocer gente nueva.",
   "empty_column.home.public_timeline": "la línea de tiempo pública",
   "empty_column.list": "No hay nada en esta lista aún. Cuando miembros de esta lista publiquen nuevos estatus, estos aparecerán qui.",
-  "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.lists": "No tienes ninguna lista. cuando crees una, se mostrará aquí.",
+  "empty_column.mutes": "Aún no has silenciado a ningún usuario.",
   "empty_column.notifications": "No tienes ninguna notificación aún. Interactúa con otros para empezar una conversación.",
   "empty_column.public": "¡No hay nada aquí! Escribe algo públicamente, o sigue usuarios de otras instancias manualmente para llenarlo",
   "follow_request.authorize": "Autorizar",
   "follow_request.reject": "Rechazar",
-  "getting_started.developers": "Developers",
+  "getting_started.developers": "Desarrolladores",
   "getting_started.documentation": "Documentation",
-  "getting_started.find_friends": "Find friends from Twitter",
+  "getting_started.find_friends": "Encuentra a tus amigos desde Twitter",
   "getting_started.heading": "Primeros pasos",
-  "getting_started.invite": "Invite people",
+  "getting_started.invite": "Invitar usuarios",
   "getting_started.open_source_notice": "Mastodon es software libre. Puedes contribuir o reportar errores en {github}.",
-  "getting_started.security": "Security",
-  "getting_started.terms": "Terms of service",
+  "getting_started.security": "Seguridad",
+  "getting_started.terms": "Términos de servicio",
   "home.column_settings.basic": "Básico",
   "home.column_settings.show_reblogs": "Mostrar retoots",
   "home.column_settings.show_replies": "Mostrar respuestas",
   "keyboard_shortcuts.back": "volver atrás",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "abrir una lista de usuarios bloqueados",
   "keyboard_shortcuts.boost": "retootear",
   "keyboard_shortcuts.column": "enfocar un estado en una de las columnas",
   "keyboard_shortcuts.compose": "enfocar el área de texto de redacción",
   "keyboard_shortcuts.description": "Description",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "abrir la columna de mensajes directos",
   "keyboard_shortcuts.down": "mover hacia abajo en la lista",
   "keyboard_shortcuts.enter": "to open status",
   "keyboard_shortcuts.favourite": "añadir a favoritos",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "abrir la lista de favoritos",
+  "keyboard_shortcuts.federated": "abrir el timeline federado",
   "keyboard_shortcuts.heading": "Keyboard Shortcuts",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "abrir el timeline propio",
   "keyboard_shortcuts.hotkey": "Tecla caliente",
   "keyboard_shortcuts.legend": "para mostrar esta leyenda",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "abrir el timeline local",
   "keyboard_shortcuts.mention": "para mencionar al autor",
-  "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.pinned": "to open pinned toots list",
-  "keyboard_shortcuts.profile": "to open author's profile",
+  "keyboard_shortcuts.muted": "abrir la lista de usuarios silenciados",
+  "keyboard_shortcuts.my_profile": "abrir tu perfil",
+  "keyboard_shortcuts.notifications": "abrir la columna de notificaciones",
+  "keyboard_shortcuts.pinned": "abrir la lista de toots destacados",
+  "keyboard_shortcuts.profile": "abrir el perfil del autor",
   "keyboard_shortcuts.reply": "para responder",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "abrir la lista de peticiones de seguidores",
   "keyboard_shortcuts.search": "para poner el foco en la búsqueda",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
-  "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW",
+  "keyboard_shortcuts.start": "abrir la columna \"comenzar\"",
+  "keyboard_shortcuts.toggle_hidden": "mostrar/ocultar texto tras aviso de contenido (CW)",
   "keyboard_shortcuts.toot": "para comenzar un nuevo toot",
   "keyboard_shortcuts.unfocus": "para retirar el foco de la caja de redacción/búsqueda",
   "keyboard_shortcuts.up": "para ir hacia arriba en la lista",
@@ -183,19 +184,19 @@
   "missing_indicator.label": "No encontrado",
   "missing_indicator.sublabel": "No se encontró este recurso",
   "mute_modal.hide_notifications": "Ocultar notificaciones de este usuario?",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "Aplicaciones móviles",
   "navigation_bar.blocks": "Usuarios bloqueados",
   "navigation_bar.community_timeline": "Historia local",
-  "navigation_bar.compose": "Compose new toot",
-  "navigation_bar.direct": "Direct messages",
-  "navigation_bar.discover": "Discover",
-  "navigation_bar.domain_blocks": "Hidden domains",
+  "navigation_bar.compose": "Escribir un nuevo toot",
+  "navigation_bar.direct": "Mensajes directos",
+  "navigation_bar.discover": "Descubrir",
+  "navigation_bar.domain_blocks": "Dominios ocultos",
   "navigation_bar.edit_profile": "Editar perfil",
   "navigation_bar.favourites": "Favoritos",
-  "navigation_bar.filters": "Muted words",
+  "navigation_bar.filters": "Palabras silenciadas",
   "navigation_bar.follow_requests": "Solicitudes para seguirte",
   "navigation_bar.info": "Información adicional",
-  "navigation_bar.keyboard_shortcuts": "Atajos de teclado",
+  "navigation_bar.keyboard_shortcuts": "Atajos",
   "navigation_bar.lists": "Listas",
   "navigation_bar.logout": "Cerrar sesión",
   "navigation_bar.mutes": "Usuarios silenciados",
@@ -203,7 +204,7 @@
   "navigation_bar.pins": "Toots fijados",
   "navigation_bar.preferences": "Preferencias",
   "navigation_bar.public_timeline": "Historia federada",
-  "navigation_bar.security": "Security",
+  "navigation_bar.security": "Seguridad",
   "notification.favourite": "{name} marcó tu estado como favorito",
   "notification.follow": "{name} te empezó a seguir",
   "notification.mention": "{name} te ha mencionado",
@@ -219,7 +220,7 @@
   "notifications.column_settings.reblog": "Retoots:",
   "notifications.column_settings.show": "Mostrar en columna",
   "notifications.column_settings.sound": "Reproducir sonido",
-  "notifications.group": "{count} notifications",
+  "notifications.group": "{count} notificaciones",
   "onboarding.done": "Listo",
   "onboarding.next": "Siguiente",
   "onboarding.page_five.public_timelines": "La línea de tiempo local muestra toots públicos de todos en {domain}. La línea de tiempo federada muestra toots públicos de cualquiera a quien la gente de {domain} siga. Estas son las líneas de tiempo públicas, una buena forma de conocer gente nueva.",
@@ -272,19 +273,19 @@
   "search_popout.tips.text": "El texto simple devuelve correspondencias de nombre, usuario y hashtag",
   "search_popout.tips.user": "usuario",
   "search_results.accounts": "Gente",
-  "search_results.hashtags": "Hashtags",
+  "search_results.hashtags": "Etiquetas",
   "search_results.statuses": "Toots",
   "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
   "standalone.public_title": "Un pequeño vistazo...",
   "status.block": "Block @{name}",
-  "status.cancel_reblog_private": "Unboost",
+  "status.cancel_reblog_private": "Des-impulsar",
   "status.cannot_reblog": "Este toot no puede retootearse",
   "status.delete": "Borrar",
-  "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.detailed_status": "Vista de conversación detallada",
+  "status.direct": "Mensaje directo a @{name}",
   "status.embed": "Incrustado",
   "status.favourite": "Favorito",
-  "status.filtered": "Filtered",
+  "status.filtered": "Filtrado",
   "status.load_more": "Cargar más",
   "status.media_hidden": "Contenido multimedia oculto",
   "status.mention": "Mencionar",
@@ -295,10 +296,10 @@
   "status.pin": "Fijar",
   "status.pinned": "Toot fijado",
   "status.reblog": "Retootear",
-  "status.reblog_private": "Boost to original audience",
+  "status.reblog_private": "Implusar a la audiencia original",
   "status.reblogged_by": "Retooteado por {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.reblogs.empty": "Nadie impulsó este toot todavía. Cuando alguien lo haga, aparecerá aqui.",
+  "status.redraft": "Borrar y volver a borrador",
   "status.reply": "Responder",
   "status.replyAll": "Responder al hilo",
   "status.report": "Reportar",
@@ -315,14 +316,14 @@
   "tabs_bar.home": "Inicio",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificaciones",
-  "tabs_bar.search": "Search",
+  "tabs_bar.search": "Buscar",
   "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking",
   "ui.beforeunload": "Tu borrador se perderá si sales de Mastodon.",
   "upload_area.title": "Arrastra y suelta para subir",
-  "upload_button.label": "Subir multimedia",
+  "upload_button.label": "Subir multimedia (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_form.description": "Describir para los usuarios con dificultad visual",
   "upload_form.focus": "Recortar",
-  "upload_form.undo": "Deshacer",
+  "upload_form.undo": "Borrar",
   "upload_progress.label": "Subiendo…",
   "video.close": "Cerrar video",
   "video.exit_fullscreen": "Salir de pantalla completa",
diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json
index a52024b15..1dc42ae68 100644
--- a/app/javascript/mastodon/locales/eu.json
+++ b/app/javascript/mastodon/locales/eu.json
@@ -10,11 +10,12 @@
   "account.endorse": "Nabarmendu profilean",
   "account.follow": "Jarraitu",
   "account.followers": "Jarraitzaileak",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "Ez du inork erabiltzaile hau jarraitzen oraindik.",
   "account.follows": "Jarraitzen",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "Erabiltzaile honek ez du inor jarraitzen oraindik.",
   "account.follows_you": "Jarraitzen zaitu",
   "account.hide_reblogs": "Ezkutatu @{name}(r)en bultzadak",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Media",
   "account.mention": "Aipatu @{name}",
   "account.moved_to": "{name} hona lekualdatu da:",
@@ -89,7 +90,7 @@
   "confirmations.mute.confirm": "Mututu",
   "confirmations.mute.message": "Ziur {name} mututu nahi duzula?",
   "confirmations.redraft.confirm": "Ezabatu eta berridatzi",
-  "confirmations.redraft.message": "Ziur mezu hau ezabatu eta berridatzi nahi duzula? Berari egindako erantzun, bultzada eta gogokoak galduko dira.",
+  "confirmations.redraft.message": "Ziur mezu hau ezabatu eta berridatzi nahi duzula? Gogokoak eta bultzadak galduko dira eta jaso dituen erantzunak umezurtz geratuko dira.",
   "confirmations.unfollow.confirm": "Utzi jarraitzeari",
   "confirmations.unfollow.message": "Ziur {name} jarraitzeari utzi nahi diozula?",
   "embed.instructions": "Txertatu mezu hau zure webgunean beheko kodea kopatuz.",
@@ -108,19 +109,19 @@
   "emoji_button.search_results": "Bilaketaren emaitzak",
   "emoji_button.symbols": "Sinboloak",
   "emoji_button.travel": "Bidaiak eta tokiak",
-  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.blocks": "Ez duzu erabiltzailerik blokeatu oraindik.",
   "empty_column.community": "Denbora-lerro lokala hutsik dago. Idatzi zerbait publikoki pilota biraka jartzeko!",
   "empty_column.direct": "Ez duzu mezu zuzenik oraindik. Baten bat bidali edo jasotzen duzunean, hemen agertuko da.",
-  "empty_column.domain_blocks": "There are no hidden 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.domain_blocks": "Ez dago ezkutatutako domeinurik oraindik.",
+  "empty_column.favourited_statuses": "Ez duzu gogokorik oraindik. Gogokoren bat duzunean hemen agertuko da.",
+  "empty_column.favourites": "Ez du inork gogokoetara gehitu toot hau oraindik. Inork egiten duenean, hemen agertuko dira.",
+  "empty_column.follow_requests": "Ez duzu jarraitzeko eskaririk oraindik. Baten bat jasotzen duzunean, hemen agertuko da.",
   "empty_column.hashtag": "Ez dago ezer traola honetan oraindik.",
   "empty_column.home": "Zure hasierako denbora-lerroa hutsik dago! Ikusi {public} edo erabili bilaketa lehen urratsak eman eta beste batzuk aurkitzeko.",
   "empty_column.home.public_timeline": "denbora-lerro publikoa",
   "empty_column.list": "Ez dago ezer zerrenda honetan. Zerrenda honetako kideek mezu berriak argitaratzean, hemen agertuko dira.",
-  "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.lists": "Ez duzu zerrendarik oraindik. Baten bat sortzen duzunean hemen agertuko da.",
+  "empty_column.mutes": "Ez duzu erabiltzailerik mututu oraindik.",
   "empty_column.notifications": "Ez duzu jakinarazpenik oraindik. Jarri besteekin harremanetan elkarrizketa abiatzeko.",
   "empty_column.public": "Ez dago ezer hemen! Idatzi zerbait publikoki edo jarraitu eskuz beste instantzia batzuetako erabiltzailean hau betetzeko",
   "follow_request.authorize": "Baimendu",
@@ -137,32 +138,32 @@
   "home.column_settings.show_reblogs": "Erakutsi bultzadak",
   "home.column_settings.show_replies": "Erakutsi erantzunak",
   "keyboard_shortcuts.back": "atzera nabigatzeko",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "blokeatutako erabiltzaileen zerrenda irekitzeko",
   "keyboard_shortcuts.boost": "bultzada ematea",
   "keyboard_shortcuts.column": "mezu bat zutabe batean fokatzea",
   "keyboard_shortcuts.compose": "testua konposatzeko arean fokatzea",
   "keyboard_shortcuts.description": "Description",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "mezu zuzenen zutabea irekitzeko",
   "keyboard_shortcuts.down": "zerrendan behera mugitzea",
   "keyboard_shortcuts.enter": "to open status",
   "keyboard_shortcuts.favourite": "gogoko egitea",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "gogokoen zerrenda irekitzeko",
+  "keyboard_shortcuts.federated": "federatutako denbora-lerroa irekitzeko",
   "keyboard_shortcuts.heading": "Keyboard Shortcuts",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "hasierako denbora-lerroa irekitzeko",
   "keyboard_shortcuts.hotkey": "Laster-tekla",
   "keyboard_shortcuts.legend": "legenda hau bistaratzea",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "denbora-lerro lokala irekitzeko",
   "keyboard_shortcuts.mention": "egilea aipatzea",
-  "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.pinned": "to open pinned toots list",
+  "keyboard_shortcuts.muted": "mutututako erabiltzaileen zerrenda irekitzeko",
+  "keyboard_shortcuts.my_profile": "zure profila irekitzeko",
+  "keyboard_shortcuts.notifications": "jakinarazpenen zutabea irekitzeko",
+  "keyboard_shortcuts.pinned": "finkatutako toot-en zerrenda irekitzeko",
   "keyboard_shortcuts.profile": "egilearen profila irekitzeko",
   "keyboard_shortcuts.reply": "erantzutea",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "jarraitzeko eskarien zerrenda irekitzeko",
   "keyboard_shortcuts.search": "bilaketan fokua jartzea",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
+  "keyboard_shortcuts.start": "\"Menua\" zutabea irekitzeko",
   "keyboard_shortcuts.toggle_hidden": "testua erakustea/ezkutatzea abisu baten atzean",
   "keyboard_shortcuts.toot": "toot berria hastea",
   "keyboard_shortcuts.unfocus": "testua konposatzeko area / bilaketatik fokua kentzea",
@@ -183,10 +184,10 @@
   "missing_indicator.label": "Ez aurkitua",
   "missing_indicator.sublabel": "Baliabide hau ezin izan da aurkitu",
   "mute_modal.hide_notifications": "Ezkutatu erabiltzaile honen jakinarazpenak?",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "Mugikorrerako aplikazioak",
   "navigation_bar.blocks": "Blokeatutako erabiltzaileak",
   "navigation_bar.community_timeline": "Denbora-lerro lokala",
-  "navigation_bar.compose": "Compose new toot",
+  "navigation_bar.compose": "Idatzi toot berria",
   "navigation_bar.direct": "Mezu zuzenak",
   "navigation_bar.discover": "Aurkitu",
   "navigation_bar.domain_blocks": "Ezkutatutako domeinuak",
@@ -280,7 +281,7 @@
   "status.cancel_reblog_private": "Kendu bultzada",
   "status.cannot_reblog": "Mezu honi ezin zaio bultzada eman",
   "status.delete": "Ezabatu",
-  "status.detailed_status": "Detailed conversation view",
+  "status.detailed_status": "Elkarrizketaren ikuspegi xehetsua",
   "status.direct": "Mezu zuzena @{name}(r)i",
   "status.embed": "Txertatu",
   "status.favourite": "Gogokoa",
@@ -297,7 +298,7 @@
   "status.reblog": "Bultzada",
   "status.reblog_private": "Bultzada jatorrizko hartzaileei",
   "status.reblogged_by": "{name}(r)en bultzada",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
+  "status.reblogs.empty": "Ez dio inork bultzada eman toot honi oraindik. Inork egiten duenean, hemen agertuko dira.",
   "status.redraft": "Ezabatu eta berridatzi",
   "status.reply": "Erantzun",
   "status.replyAll": "Erantzun harian",
@@ -319,7 +320,7 @@
   "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} hitz egiten",
   "ui.beforeunload": "Zure zirriborroa galduko da Mastodon uzten baduzu.",
   "upload_area.title": "Arrastatu eta jaregin igotzeko",
-  "upload_button.label": "Gehitu multimedia",
+  "upload_button.label": "Gehitu multimedia  (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_form.description": "Deskribatu ikusmen arazoak dituztenentzat",
   "upload_form.focus": "Moztu",
   "upload_form.undo": "Ezabatu",
diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json
index f57c1354e..2e3999918 100644
--- a/app/javascript/mastodon/locales/fa.json
+++ b/app/javascript/mastodon/locales/fa.json
@@ -10,11 +10,12 @@
   "account.endorse": "نمایش در نمایه",
   "account.follow": "پی بگیرید",
   "account.followers": "پیگیران",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "هنوز هیچ کسی پیگیر این کاربر نیست.",
   "account.follows": "پی می‌گیرد",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "این کاربر هنوز هیچ کسی را پی نمی‌گیرد.",
   "account.follows_you": "پیگیر شماست",
   "account.hide_reblogs": "پنهان کردن بازبوق‌های @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "عکس و ویدیو",
   "account.mention": "نام‌بردن از @{name}",
   "account.moved_to": "{name} منتقل شده است به:",
@@ -108,19 +109,19 @@
   "emoji_button.search_results": "نتایج جستجو",
   "emoji_button.symbols": "نمادها",
   "emoji_button.travel": "سفر و مکان",
-  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.blocks": "شما هنوز هیچ کسی را مسدود نکرده‌اید.",
   "empty_column.community": "فهرست نوشته‌های محلی خالی است. چیزی بنویسید تا چرخش بچرخد!",
   "empty_column.direct": "شما هیچ پیغام مستقیمی ندارید. اگر چنین پیغامی بگیرید یا بفرستید این‌جا نمایش خواهد یافت.",
-  "empty_column.domain_blocks": "There are no hidden 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.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": "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.lists": "شما هنوز هیچ فهرستی ندارید. اگر فهرستی بسازید، این‌جا نمایش خواهد یافت.",
+  "empty_column.mutes": "شما هنوز هیچ کاربری را بی‌صدا نکرده‌اید.",
   "empty_column.notifications": "هنوز هیچ اعلانی ندارید. به نوشته‌های دیگران واکنش نشان دهید تا گفتگو آغاز شود.",
   "empty_column.public": "این‌جا هنوز چیزی نیست! خودتان چیزی بنویسید یا کاربران دیگر را پی بگیرید تا این‌جا پر شود",
   "follow_request.authorize": "اجازه دهید",
@@ -137,32 +138,32 @@
   "home.column_settings.show_reblogs": "نمایش بازبوق‌ها",
   "home.column_settings.show_replies": "نمایش پاسخ‌ها",
   "keyboard_shortcuts.back": "برای بازگشت",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "برای گشودن کاربران بی‌صداشده",
   "keyboard_shortcuts.boost": "برای بازبوقیدن",
   "keyboard_shortcuts.column": "برای برجسته‌کردن یک نوشته در یکی از ستون‌ها",
   "keyboard_shortcuts.compose": "برای فعال‌کردن کادر نوشتهٔ تازه",
   "keyboard_shortcuts.description": "توضیح",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "برای گشودن ستون پیغام‌های مستقیم",
   "keyboard_shortcuts.down": "برای پایین‌رفتن در فهرست",
   "keyboard_shortcuts.enter": "برای گشودن نوشته",
   "keyboard_shortcuts.favourite": "برای پسندیدن",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "برای گشودن پیغام‌های پسندیده‌شده",
+  "keyboard_shortcuts.federated": "برای گشودن فهرست نوشته‌های همه‌جا",
   "keyboard_shortcuts.heading": "میان‌برهای صفحه‌کلید",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "برای گشودن ستون اصلی پیگیری‌ها",
   "keyboard_shortcuts.hotkey": "میان‌بر",
   "keyboard_shortcuts.legend": "برای نمایش این راهنما",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "برای گشودن فهرست نوشته‌های محلی",
   "keyboard_shortcuts.mention": "برای نام‌بردن از نویسنده",
-  "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.pinned": "to open pinned toots list",
+  "keyboard_shortcuts.muted": "برای گشودن فهرست کاربران بی‌صداشده",
+  "keyboard_shortcuts.my_profile": "برای گشودن صفحهٔ نمایهٔ شما",
+  "keyboard_shortcuts.notifications": "برای گشودن ستون اعلان‌ها",
+  "keyboard_shortcuts.pinned": "برای گشودن فهرست نوشته‌‌های ثابت",
   "keyboard_shortcuts.profile": "گشودن نمایهٔ نویسنده",
   "keyboard_shortcuts.reply": "برای پاسخ‌دادن",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "برای گشودن فهرست درخواست‌های پیگیری",
   "keyboard_shortcuts.search": "برای فعال‌کردن جستجو",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
+  "keyboard_shortcuts.start": "برای گشودن ستون «آغاز کنید»",
   "keyboard_shortcuts.toggle_hidden": "برای نمایش/نهفتن نوشتهٔ پشت هشدار محتوا",
   "keyboard_shortcuts.toot": "برای آغاز یک بوق تازه",
   "keyboard_shortcuts.unfocus": "برای برداشتن توجه از نوشتن/جستجو",
@@ -183,11 +184,11 @@
   "missing_indicator.label": "پیدا نشد",
   "missing_indicator.sublabel": "این منبع پیدا نشد",
   "mute_modal.hide_notifications": "اعلان‌های این کاربر پنهان شود؟",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "اپ‌های موبایل",
   "navigation_bar.blocks": "کاربران مسدودشده",
   "navigation_bar.community_timeline": "نوشته‌های محلی",
-  "navigation_bar.compose": "Compose new toot",
-  "navigation_bar.direct": "پیغام‌های خصوصی",
+  "navigation_bar.compose": "نوشتن بوق تازه",
+  "navigation_bar.direct": "پیغام‌های مستقیم",
   "navigation_bar.discover": "گشت و گذار",
   "navigation_bar.domain_blocks": "دامین‌های پنهان‌شده",
   "navigation_bar.edit_profile": "ویرایش نمایه",
@@ -280,7 +281,7 @@
   "status.cancel_reblog_private": "حذف بازبوق",
   "status.cannot_reblog": "این نوشته را نمی‌شود بازبوقید",
   "status.delete": "پاک‌کردن",
-  "status.detailed_status": "Detailed conversation view",
+  "status.detailed_status": "نمایش کامل گفتگو",
   "status.direct": "پیغام مستقیم به @{name}",
   "status.embed": "جاگذاری",
   "status.favourite": "پسندیدن",
@@ -297,7 +298,7 @@
   "status.reblog": "بازبوقیدن",
   "status.reblog_private": "بازبوق به مخاطبان اولیه",
   "status.reblogged_by": "‫{name}‬ بازبوقید",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
+  "status.reblogs.empty": "هنوز هیچ کسی این بوق را بازنبوقیده است. وقتی کسی چنین کاری کند، این‌جا نمایش خواهد یافت.",
   "status.redraft": "پاک‌کردن و بازنویسی",
   "status.reply": "پاسخ",
   "status.replyAll": "به نوشته پاسخ دهید",
@@ -319,7 +320,7 @@
   "trends.count_by_accounts": "{count} {rawCount, plural, one {نفر نوشته است} other {نفر نوشته‌اند}}",
   "ui.beforeunload": "اگر از ماستدون خارج شوید پیش‌نویس شما پاک خواهد شد.",
   "upload_area.title": "برای بارگذاری به این‌جا بکشید",
-  "upload_button.label": "افزودن عکس و ویدیو (JPEG, PNG, GIF, WebM, MP4)",
+  "upload_button.label": "افزودن عکس و ویدیو (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_form.description": "نوشتهٔ توضیحی برای کم‌بینایان و نابینایان",
   "upload_form.focus": "بریدن لبه‌ها",
   "upload_form.undo": "حذف",
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index fd23e7ff5..f6c0469be 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "Seuraa sinua",
   "account.hide_reblogs": "Piilota buustaukset käyttäjältä @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Media",
   "account.mention": "Mainitse @{name}",
   "account.moved_to": "{name} on muuttanut instanssiin:",
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index 41eaab417..075bdcf30 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -10,11 +10,12 @@
   "account.endorse": "Figure sur le profil",
   "account.follow": "Suivre",
   "account.followers": "Abonné⋅e⋅s",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "Personne ne suit cet utilisateur pour l'instant.",
   "account.follows": "Abonnements",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "Cet utilisateur ne suit personne pour l'instant.",
   "account.follows_you": "Vous suit",
   "account.hide_reblogs": "Masquer les partages de @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Média",
   "account.mention": "Mentionner",
   "account.moved_to": "{name} a déménagé vers :",
@@ -108,19 +109,19 @@
   "emoji_button.search_results": "Résultats de la recherche",
   "emoji_button.symbols": "Symboles",
   "emoji_button.travel": "Lieux & Voyages",
-  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.blocks": "Vous n'avez bloqué aucun utilisateur pour le moment.",
   "empty_column.community": "Le fil public local est vide. Écrivez donc quelque chose pour le remplir !",
   "empty_column.direct": "Vous n’avez pas encore de messages directs. Lorsque vous en enverrez ou recevrez un, il s’affichera ici.",
-  "empty_column.domain_blocks": "There are no hidden 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.domain_blocks": "Il n'y a aucun domaine caché pour le moment.",
+  "empty_column.favourited_statuses": "Vous n'avez aucun pouet favoris pour le moment. Lorsque vous en mettrez un en favori, il apparaîtra ici.",
+  "empty_column.favourites": "Personne n'a encore mis ce pouet en favori. Lorsque quelqu'un le fera, il apparaîtra ici.",
+  "empty_column.follow_requests": "Vous n'avez pas encore de demande de suivi. Lorsque vous en recevrez une, elle apparaîtra ici.",
   "empty_column.hashtag": "Il n’y a encore aucun contenu associé à ce hashtag.",
   "empty_column.home": "Vous ne suivez personne. Visitez {public} ou utilisez la recherche pour trouver d’autres personnes à suivre.",
   "empty_column.home.public_timeline": "le fil public",
   "empty_column.list": "Il n’y a rien dans cette liste pour l’instant. Dès que des personnes de cette liste publieront de nouveaux statuts, ils apparaîtront ici.",
-  "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.lists": "Vous n'avez pas encore de liste. Lorsque vous en créerez une, elle apparaîtra ici.",
+  "empty_column.mutes": "Vous n'avez pas encore mis des utilisateurs en silence.",
   "empty_column.notifications": "Vous n’avez pas encore de notification. Interagissez avec d’autres personnes pour débuter la conversation.",
   "empty_column.public": "Il n’y a rien ici ! Écrivez quelque chose publiquement, ou bien suivez manuellement des personnes d’autres instances pour remplir le fil public",
   "follow_request.authorize": "Accepter",
@@ -137,32 +138,32 @@
   "home.column_settings.show_reblogs": "Afficher les partages",
   "home.column_settings.show_replies": "Afficher les réponses",
   "keyboard_shortcuts.back": "revenir en arrière",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "pour ouvrir une liste d'utilisateurs bloqués",
   "keyboard_shortcuts.boost": "partager",
   "keyboard_shortcuts.column": "focaliser un statut dans l’une des colonnes",
   "keyboard_shortcuts.compose": "pour centrer la zone de rédaction",
   "keyboard_shortcuts.description": "Description",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "pour ouvrir une colonne des messages directs",
   "keyboard_shortcuts.down": "pour descendre dans la liste",
   "keyboard_shortcuts.enter": "pour ouvrir le statut",
   "keyboard_shortcuts.favourite": "vers les favoris",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "pour ouvrir une liste de favoris",
+  "keyboard_shortcuts.federated": "pour ouvrir le fil public global",
   "keyboard_shortcuts.heading": "Raccourcis clavier",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "pour ouvrir l'accueil",
   "keyboard_shortcuts.hotkey": "Raccourci",
   "keyboard_shortcuts.legend": "pour afficher cette légende",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "pour ouvrir le fil public local",
   "keyboard_shortcuts.mention": "pour mentionner l’auteur·rice",
-  "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.pinned": "to open pinned toots list",
+  "keyboard_shortcuts.muted": "pour ouvrir la liste des utilisateurs rendus muets",
+  "keyboard_shortcuts.my_profile": "pour ouvrir votre profil",
+  "keyboard_shortcuts.notifications": "pour ouvrir votre colonne de notifications",
+  "keyboard_shortcuts.pinned": "pour ouvrir une liste des pouets épinglés",
   "keyboard_shortcuts.profile": "pour ouvrir le profil de l’auteur·rice",
   "keyboard_shortcuts.reply": "pour répondre",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "pour ouvrir la liste de demandes de suivi",
   "keyboard_shortcuts.search": "pour cibler la recherche",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
+  "keyboard_shortcuts.start": "pour ouvrir la colonne \"pour commencer\"",
   "keyboard_shortcuts.toggle_hidden": "pour afficher/cacher un texte derrière CW",
   "keyboard_shortcuts.toot": "pour démarrer un tout nouveau pouet",
   "keyboard_shortcuts.unfocus": "pour recentrer composer textarea/search",
@@ -297,7 +298,7 @@
   "status.reblog": "Partager",
   "status.reblog_private": "Booster vers l’audience originale",
   "status.reblogged_by": "{name} a partagé :",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
+  "status.reblogs.empty": "Personne n'a encore partagé ce pouet. Lorsque quelqu'un le fera, il apparaîtra ici.",
   "status.redraft": "Effacer et ré-écrire",
   "status.reply": "Répondre",
   "status.replyAll": "Répondre au fil",
@@ -319,7 +320,7 @@
   "trends.count_by_accounts": "{count} {rawCount, plural, one {personne} other {personnes}} discutent",
   "ui.beforeunload": "Votre brouillon sera perdu si vous quittez Mastodon.",
   "upload_area.title": "Glissez et déposez pour envoyer",
-  "upload_button.label": "Joindre un média (JPEG, PNG, GIF, WebM, MP4)",
+  "upload_button.label": "Joindre un média (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_form.description": "Décrire pour les malvoyant·e·s",
   "upload_form.focus": "Recadrer",
   "upload_form.undo": "Supprimer",
diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json
index 26c99e541..8b830ae7b 100644
--- a/app/javascript/mastodon/locales/gl.json
+++ b/app/javascript/mastodon/locales/gl.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "Séguena",
   "account.hide_reblogs": "Ocultar repeticións de @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Medios",
   "account.mention": "Mencionar @{name}",
   "account.moved_to": "{name} marchou a:",
@@ -89,7 +90,7 @@
   "confirmations.mute.confirm": "Acalar",
   "confirmations.mute.message": "Está segura de que quere acalar a {name}?",
   "confirmations.redraft.confirm": "Eliminar e reescribir",
-  "confirmations.redraft.message": "Está segura de querer eliminar este estado e voltalo a escribir? Perderá todas as réplicas, promocións e favoritas da mensaxe.",
+  "confirmations.redraft.message": "Está segura de querer eliminar este estado e voltalo a escribir? Perderá réplicas e favoritas, e as respostas ao orixinal quedarán orfas.",
   "confirmations.unfollow.confirm": "Deixar de seguir",
   "confirmations.unfollow.message": "Quere deixar de seguir a {name}?",
   "embed.instructions": "Copie o código inferior para incrustar no seu sitio web este estado.",
@@ -319,7 +320,7 @@
   "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking",
   "ui.beforeunload": "O borrador perderase se sae de Mastodon.",
   "upload_area.title": "Arrastre e solte para subir",
-  "upload_button.label": "Engadir medios",
+  "upload_button.label": "Engadir medios (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_form.description": "Describa para deficientes visuais",
   "upload_form.focus": "Recortar",
   "upload_form.undo": "Eliminar",
diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json
index e9a13b02d..4aee82212 100644
--- a/app/javascript/mastodon/locales/he.json
+++ b/app/javascript/mastodon/locales/he.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "במעקב אחריך",
   "account.hide_reblogs": "להסתיר הידהודים מאת @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "מדיה",
   "account.mention": "אזכור של @{name}",
   "account.moved_to": "החשבון {name} הועבר אל:",
diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json
index 7da45c3a4..a138cf589 100644
--- a/app/javascript/mastodon/locales/hr.json
+++ b/app/javascript/mastodon/locales/hr.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "te slijedi",
   "account.hide_reblogs": "Hide boosts from @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Media",
   "account.mention": "Spomeni @{name}",
   "account.moved_to": "{name} has moved to:",
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
index c9d32135a..6a4d0ecf0 100644
--- a/app/javascript/mastodon/locales/hu.json
+++ b/app/javascript/mastodon/locales/hu.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "Követnek téged",
   "account.hide_reblogs": "Rejtsd el a tülkölést @{name}-tól/től",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Média",
   "account.mention": "@{name} említése",
   "account.moved_to": "{name} átköltözött:",
diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json
index 1376f6894..7d7090236 100644
--- a/app/javascript/mastodon/locales/hy.json
+++ b/app/javascript/mastodon/locales/hy.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "Հետեւում է քեզ",
   "account.hide_reblogs": "Թաքցնել @{name}֊ի տարածածները",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Մեդիա",
   "account.mention": "Նշել @{name}֊ին",
   "account.moved_to": "{name}֊ը տեղափոխվել է՝",
diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json
index e8b087e9d..c8ed00e9f 100644
--- a/app/javascript/mastodon/locales/id.json
+++ b/app/javascript/mastodon/locales/id.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "Mengikuti anda",
   "account.hide_reblogs": "Sembunyikan boosts dari @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Media",
   "account.mention": "Balasan @{name}",
   "account.moved_to": "{name} telah pindah ke:",
diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json
index a3ea121e4..c92ceccd1 100644
--- a/app/javascript/mastodon/locales/io.json
+++ b/app/javascript/mastodon/locales/io.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "Sequas tu",
   "account.hide_reblogs": "Hide boosts from @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Media",
   "account.mention": "Mencionar @{name}",
   "account.moved_to": "{name} has moved to:",
diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json
index fa0956fe3..e9eb62e71 100644
--- a/app/javascript/mastodon/locales/it.json
+++ b/app/javascript/mastodon/locales/it.json
@@ -7,14 +7,15 @@
   "account.disclaimer_full": "Il profilo dell'utente mostrato qui sotto potrebbe essere incompleto.",
   "account.domain_blocked": "Dominio nascosto",
   "account.edit_profile": "Modifica profilo",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "Metti in evidenza sul profilo",
   "account.follow": "Segui",
   "account.followers": "Seguaci",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "Ancora nessuno segue questo utente.",
   "account.follows": "Segue",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "Questo utente non segue ancora nessuno.",
   "account.follows_you": "Ti segue",
   "account.hide_reblogs": "Nascondi condivisioni da @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Media",
   "account.mention": "Menziona @{name}",
   "account.moved_to": "{name} si è trasferito su:",
@@ -29,7 +30,7 @@
   "account.show_reblogs": "Mostra condivisioni da @{name}",
   "account.unblock": "Sblocca @{name}",
   "account.unblock_domain": "Non nascondere {domain}",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "Non mettere in evidenza sul profilo",
   "account.unfollow": "Non seguire",
   "account.unmute": "Non silenziare @{name}",
   "account.unmute_notifications": "Non silenziare più le notifiche da @{name}",
@@ -108,19 +109,19 @@
   "emoji_button.search_results": "Risultati della ricerca",
   "emoji_button.symbols": "Simboli",
   "emoji_button.travel": "Viaggi e luoghi",
-  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.blocks": "Non hai ancora bloccato nessun utente.",
   "empty_column.community": "La timeline locale è vuota. Condividi qualcosa pubblicamente per dare inizio alla festa!",
   "empty_column.direct": "Non hai ancora nessun messaggio diretto. Quando ne manderai o riceverai qualcuno, apparirà qui.",
-  "empty_column.domain_blocks": "There are no hidden 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.domain_blocks": "Non vi sono domini nascosti.",
+  "empty_column.favourited_statuses": "Non hai ancora segnato nessun toot come apprezzato. Quando lo farai, comparirà qui.",
+  "empty_column.favourites": "Nessuno ha ancora segnato questo toot come apprezzato. Quando qualcuno lo farà, apparirà qui.",
+  "empty_column.follow_requests": "Non hai ancora ricevuto nessuna richiesta di seguirti. Quando ne arriveranno, saranno mostrate qui.",
   "empty_column.hashtag": "Non c'è ancora nessun post con questo hashtag.",
   "empty_column.home": "Non stai ancora seguendo nessuno. Visita {public} o usa la ricerca per incontrare nuove persone.",
   "empty_column.home.public_timeline": "la timeline pubblica",
   "empty_column.list": "Non c'è niente in questo elenco ancora. Quando i membri di questo elenco postano nuovi stati, questi appariranno qui.",
-  "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.lists": "Non hai ancora nessuna lista. Quando ne creerai qualcuna, comparirà qui.",
+  "empty_column.mutes": "Non hai ancora silenziato nessun utente.",
   "empty_column.notifications": "Non hai ancora nessuna notifica. Interagisci con altri per iniziare conversazioni.",
   "empty_column.public": "Qui non c'è nulla! Scrivi qualcosa pubblicamente, o aggiungi utenti da altri server per riempire questo spazio",
   "follow_request.authorize": "Autorizza",
@@ -137,32 +138,32 @@
   "home.column_settings.show_reblogs": "Mostra post condivisi",
   "home.column_settings.show_replies": "Mostra risposte",
   "keyboard_shortcuts.back": "per tornare indietro",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "per aprire l'elenco degli utenti bloccati",
   "keyboard_shortcuts.boost": "per condividere",
   "keyboard_shortcuts.column": "per portare il focus su uno status in una delle colonne",
   "keyboard_shortcuts.compose": "per portare il focus nell'area di composizione",
   "keyboard_shortcuts.description": "Descrizione",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "per aprire la colonna dei messaggi diretti",
   "keyboard_shortcuts.down": "per spostarsi in basso nella lista",
   "keyboard_shortcuts.enter": "per aprire lo status",
   "keyboard_shortcuts.favourite": "per segnare come apprezzato",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "per aprire l'elenco dei toot apprezzati",
+  "keyboard_shortcuts.federated": "per aprire la timeline federata",
   "keyboard_shortcuts.heading": "Tasti di scelta rapida",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "per aprire la timeline home",
   "keyboard_shortcuts.hotkey": "Tasto di scelta rapida",
   "keyboard_shortcuts.legend": "per mostrare questa spiegazione",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "per aprire la timeline locale",
   "keyboard_shortcuts.mention": "per menzionare l'autore",
-  "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.pinned": "to open pinned toots list",
+  "keyboard_shortcuts.muted": "per aprire l'elenco degli utenti silenziati",
+  "keyboard_shortcuts.my_profile": "per aprire il tuo profilo",
+  "keyboard_shortcuts.notifications": "per aprire la colonna delle notifiche",
+  "keyboard_shortcuts.pinned": "per aprire l'elenco dei toot fissati in cima",
   "keyboard_shortcuts.profile": "per aprire il profilo dell'autore",
   "keyboard_shortcuts.reply": "per rispondere",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "per aprire l'elenco delle richieste di seguirti",
   "keyboard_shortcuts.search": "per spostare il focus sulla ricerca",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
+  "keyboard_shortcuts.start": "per aprire la colonna \"Come iniziare\"",
   "keyboard_shortcuts.toggle_hidden": "per mostrare/nascondere il testo dei CW",
   "keyboard_shortcuts.toot": "per iniziare a scrivere un toot completamente nuovo",
   "keyboard_shortcuts.unfocus": "per uscire dall'area di composizione o dalla ricerca",
@@ -183,10 +184,10 @@
   "missing_indicator.label": "Non trovato",
   "missing_indicator.sublabel": "Risorsa non trovata",
   "mute_modal.hide_notifications": "Nascondere le notifiche da quest'utente?",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "App per dispositivi mobili",
   "navigation_bar.blocks": "Utenti bloccati",
   "navigation_bar.community_timeline": "Timeline locale",
-  "navigation_bar.compose": "Compose new toot",
+  "navigation_bar.compose": "Componi nuovo toot",
   "navigation_bar.direct": "Messaggi diretti",
   "navigation_bar.discover": "Scopri",
   "navigation_bar.domain_blocks": "Domini nascosti",
@@ -280,7 +281,7 @@
   "status.cancel_reblog_private": "Annulla condivisione",
   "status.cannot_reblog": "Questo post non può essere condiviso",
   "status.delete": "Elimina",
-  "status.detailed_status": "Detailed conversation view",
+  "status.detailed_status": "Vista conversazione dettagliata",
   "status.direct": "Messaggio diretto @{name}",
   "status.embed": "Incorpora",
   "status.favourite": "Apprezzato",
@@ -297,7 +298,7 @@
   "status.reblog": "Condividi",
   "status.reblog_private": "Condividi con i destinatari iniziali",
   "status.reblogged_by": "{name} ha condiviso",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
+  "status.reblogs.empty": "Nessuno ha ancora condiviso questo toot. Quando qualcuno lo farà, comparirà qui.",
   "status.redraft": "Cancella e riscrivi",
   "status.reply": "Rispondi",
   "status.replyAll": "Rispondi alla conversazione",
diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json
index c1eada447..04a2e4692 100644
--- a/app/javascript/mastodon/locales/ja.json
+++ b/app/javascript/mastodon/locales/ja.json
@@ -10,11 +10,12 @@
   "account.endorse": "プロフィールで紹介する",
   "account.follow": "フォロー",
   "account.followers": "フォロワー",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "まだ誰もフォローしていません。",
   "account.follows": "フォロー",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "まだ誰もフォローしていません。",
   "account.follows_you": "フォローされています",
   "account.hide_reblogs": "@{name}さんからのブーストを非表示",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "メディア",
   "account.mention": "@{name}さんにトゥート",
   "account.moved_to": "{name}さんは引っ越しました:",
@@ -93,7 +94,7 @@
   "confirmations.mute.confirm": "ミュート",
   "confirmations.mute.message": "本当に{name}さんをミュートしますか?",
   "confirmations.redraft.confirm": "削除して下書きに戻す",
-  "confirmations.redraft.message": "本当にこのトゥートを削除して下書きに戻しますか? このトゥートへのお気に入り登録やブーストは失われ、リプライは孤立することになります。",
+  "confirmations.redraft.message": "本当にこのトゥートを削除して下書きに戻しますか? このトゥートへのお気に入り登録やブーストは失われ、返信は孤立することになります。",
   "confirmations.unfollow.confirm": "フォロー解除",
   "confirmations.unfollow.message": "本当に{name}さんのフォローを解除しますか?",
   "embed.instructions": "下記のコードをコピーしてウェブサイトに埋め込みます。",
@@ -112,19 +113,19 @@
   "emoji_button.search_results": "検索結果",
   "emoji_button.symbols": "記号",
   "emoji_button.travel": "旅行と場所",
-  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.blocks": "まだ誰もブロックしていません。",
   "empty_column.community": "ローカルタイムラインはまだ使われていません。何か書いてみましょう!",
   "empty_column.direct": "ダイレクトメッセージはまだありません。ダイレクトメッセージをやりとりすると、ここに表示されます。",
-  "empty_column.domain_blocks": "There are no hidden 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.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": "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.lists": "まだリストがありません。リストを作るとここに表示されます。",
+  "empty_column.mutes": "まだ誰もミュートしていません。",
   "empty_column.notifications": "まだ通知がありません。他の人とふれ合って会話を始めましょう。",
   "empty_column.public": "ここにはまだ何もありません! 公開で何かを投稿したり、他のインスタンスのユーザーをフォローしたりしていっぱいにしましょう",
   "follow_request.authorize": "許可",
@@ -141,32 +142,32 @@
   "home.column_settings.show_reblogs": "ブースト表示",
   "home.column_settings.show_replies": "返信表示",
   "keyboard_shortcuts.back": "戻る",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "ブロックしたユーザーのリストを開く",
   "keyboard_shortcuts.boost": "ブースト",
   "keyboard_shortcuts.column": "左からn番目のカラム内最新トゥートに移動",
   "keyboard_shortcuts.compose": "トゥート入力欄に移動",
   "keyboard_shortcuts.description": "説明",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "ダイレクトメッセージのカラムを開く",
   "keyboard_shortcuts.down": "カラム内一つ下に移動",
   "keyboard_shortcuts.enter": "トゥートの詳細を表示",
   "keyboard_shortcuts.favourite": "お気に入り",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "お気に入り登録のリストを開く",
+  "keyboard_shortcuts.federated": "連合タイムラインを開く",
   "keyboard_shortcuts.heading": "キーボードショートカット",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "ホームタイムラインを開く",
   "keyboard_shortcuts.hotkey": "ホットキー",
   "keyboard_shortcuts.legend": "この一覧を表示",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "ローカルタイムラインを開く",
   "keyboard_shortcuts.mention": "メンション",
-  "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.pinned": "to open pinned toots list",
+  "keyboard_shortcuts.muted": "ミュートしたユーザーのリストを開く",
+  "keyboard_shortcuts.my_profile": "自分のプロフィールを開く",
+  "keyboard_shortcuts.notifications": "通知カラムを開く",
+  "keyboard_shortcuts.pinned": "固定したトゥートのリストを開く",
   "keyboard_shortcuts.profile": "プロフィールを開く",
   "keyboard_shortcuts.reply": "返信",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "フォローリクエストのリストを開く",
   "keyboard_shortcuts.search": "検索欄に移動",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
+  "keyboard_shortcuts.start": "\"スタート\" カラムを開く",
   "keyboard_shortcuts.toggle_hidden": "CWで隠れた文を見る/隠す",
   "keyboard_shortcuts.toot": "新規トゥート",
   "keyboard_shortcuts.unfocus": "トゥート入力欄・検索欄から離れる",
@@ -190,7 +191,7 @@
   "navigation_bar.apps": "アプリ",
   "navigation_bar.blocks": "ブロックしたユーザー",
   "navigation_bar.community_timeline": "ローカルタイムライン",
-  "navigation_bar.compose": "Compose new toot",
+  "navigation_bar.compose": "トゥートの新規作成",
   "navigation_bar.direct": "ダイレクトメッセージ",
   "navigation_bar.discover": "見つける",
   "navigation_bar.domain_blocks": "非表示にしたドメイン",
@@ -285,7 +286,7 @@
   "status.cancel_reblog_private": "ブースト解除",
   "status.cannot_reblog": "この投稿はブーストできません",
   "status.delete": "削除",
-  "status.detailed_status": "Detailed conversation view",
+  "status.detailed_status": "詳細な会話ビュー",
   "status.direct": "@{name}さんにダイレクトメッセージ",
   "status.embed": "埋め込み",
   "status.favourite": "お気に入り",
@@ -302,7 +303,7 @@
   "status.reblog": "ブースト",
   "status.reblog_private": "ブースト",
   "status.reblogged_by": "{name}さんがブースト",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
+  "status.reblogs.empty": "まだ誰もブーストしていません。ブーストされるとここに表示されます。",
   "status.redraft": "削除して下書きに戻す",
   "status.reply": "返信",
   "status.replyAll": "全員に返信",
@@ -324,7 +325,7 @@
   "trends.count_by_accounts": "{count} {rawCount, plural, one {人} other {人}} がトゥート",
   "ui.beforeunload": "Mastodonから離れると送信前の投稿は失われます。",
   "upload_area.title": "ドラッグ&ドロップでアップロード",
-  "upload_button.label": "メディアを追加 (JPEG, PNG, GIF, WebM, MP4)",
+  "upload_button.label": "メディアを追加 (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_form.description": "視覚障害者のための説明",
   "upload_form.focus": "焦点",
   "upload_form.undo": "削除",
diff --git a/app/javascript/mastodon/locales/ka.json b/app/javascript/mastodon/locales/ka.json
index 6ad129254..ec6e211c6 100644
--- a/app/javascript/mastodon/locales/ka.json
+++ b/app/javascript/mastodon/locales/ka.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "მოგყვებათ",
   "account.hide_reblogs": "დაიმალოს ბუსტები @{name}-სგან",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "მედია",
   "account.mention": "ასახელეთ @{name}",
   "account.moved_to": "{name} გადავიდა:",
diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json
index c2c25fe50..15e8524e0 100644
--- a/app/javascript/mastodon/locales/ko.json
+++ b/app/javascript/mastodon/locales/ko.json
@@ -10,11 +10,12 @@
   "account.endorse": "프로필에 나타내기",
   "account.follow": "팔로우",
   "account.followers": "팔로워",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "아직 아무도 이 유저를 팔로우 하고 있지 않습니다.",
   "account.follows": "팔로우",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "이 유저는 아직 아무도 팔로우 하고 있지 않습니다.",
   "account.follows_you": "날 팔로우합니다",
   "account.hide_reblogs": "@{name}의 부스트를 숨기기",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "미디어",
   "account.mention": "@{name}에게 글쓰기",
   "account.moved_to": "{name}는 계정을 이동했습니다:",
@@ -89,7 +90,7 @@
   "confirmations.mute.confirm": "뮤트",
   "confirmations.mute.message": "정말로 {name}를 뮤트하시겠습니까?",
   "confirmations.redraft.confirm": "삭제하고 다시 쓰기",
-  "confirmations.redraft.message": "정말로 이 포스트를 삭제하고 다시 쓰시겠습니까? 해당 포스트에 대한 답장과 부스트, 그리고 즐겨찾기를 잃게 됩니다.",
+  "confirmations.redraft.message": "정말로 이 포스트를 삭제하고 다시 쓰시겠습니까? 해당 포스트에 대한 부스트와 즐겨찾기를 잃게 되고 원본에 대한 답장은 연결 되지 않습니다.",
   "confirmations.unfollow.confirm": "언팔로우",
   "confirmations.unfollow.message": "정말로 {name}를 언팔로우하시겠습니까?",
   "embed.instructions": "아래의 코드를 복사하여 대화를 원하는 곳으로 공유하세요.",
@@ -108,19 +109,19 @@
   "emoji_button.search_results": "검색 결과",
   "emoji_button.symbols": "기호",
   "emoji_button.travel": "여행과 장소",
-  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.blocks": "아직 아무도 차단하지 않았습니다.",
   "empty_column.community": "로컬 타임라인에 아무 것도 없습니다. 아무거나 적어 보세요!",
   "empty_column.direct": "아직 다이렉트 메시지가 없습니다. 다이렉트 메시지를 보내거나 받은 경우, 여기에 표시 됩니다.",
-  "empty_column.domain_blocks": "There are no hidden 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.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": "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.lists": "아직 리스트가 없습니다. 리스트를 만들면 여기에 나타납니다.",
+  "empty_column.mutes": "아직 아무도 뮤트하지 않았습니다.",
   "empty_column.notifications": "아직 알림이 없습니다. 다른 사람과 대화를 시작해 보세요.",
   "empty_column.public": "여기엔 아직 아무 것도 없습니다! 공개적으로 무언가 포스팅하거나, 다른 인스턴스의 유저를 팔로우 해서 채워보세요",
   "follow_request.authorize": "허가",
@@ -137,32 +138,32 @@
   "home.column_settings.show_reblogs": "부스트 표시",
   "home.column_settings.show_replies": "답글 표시",
   "keyboard_shortcuts.back": "뒤로가기",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "차단한 유저 리스트 열기",
   "keyboard_shortcuts.boost": "부스트",
   "keyboard_shortcuts.column": "해당 열에 포커스",
   "keyboard_shortcuts.compose": "작성창으로 포커스",
   "keyboard_shortcuts.description": "설명",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "다이렉트 메시지 컬럼 열기",
   "keyboard_shortcuts.down": "리스트에서 아래로 이동",
   "keyboard_shortcuts.enter": "열기",
   "keyboard_shortcuts.favourite": "관심글 지정",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "즐겨찾기 리스트 열기",
+  "keyboard_shortcuts.federated": "연합 타임라인 열기",
   "keyboard_shortcuts.heading": "키보드 단축키",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "홈 타임라인 열기",
   "keyboard_shortcuts.hotkey": "핫키",
   "keyboard_shortcuts.legend": "이 도움말 표시",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "로컬 타임라인 열기",
   "keyboard_shortcuts.mention": "멘션",
-  "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.pinned": "to open pinned toots list",
+  "keyboard_shortcuts.muted": "뮤트 된 유저 리스트 열기",
+  "keyboard_shortcuts.my_profile": "내 프로필 열기",
+  "keyboard_shortcuts.notifications": "알림 컬럼 열기",
+  "keyboard_shortcuts.pinned": "고정 툿 리스트 열기",
   "keyboard_shortcuts.profile": "프로필 열기",
   "keyboard_shortcuts.reply": "답장",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "팔로우 요청 리스트 열기",
   "keyboard_shortcuts.search": "검색창에 포커스",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
+  "keyboard_shortcuts.start": "\"시작하기\" 컬럼 열기",
   "keyboard_shortcuts.toggle_hidden": "CW로 가려진 텍스트를 표시/비표시",
   "keyboard_shortcuts.toot": "새 툿 작성",
   "keyboard_shortcuts.unfocus": "작성창에서 포커스 해제",
@@ -186,7 +187,7 @@
   "navigation_bar.apps": "모바일 앱",
   "navigation_bar.blocks": "차단한 사용자",
   "navigation_bar.community_timeline": "로컬 타임라인",
-  "navigation_bar.compose": "Compose new toot",
+  "navigation_bar.compose": "새 툿 작성",
   "navigation_bar.direct": "다이렉트 메시지",
   "navigation_bar.discover": "발견하기",
   "navigation_bar.domain_blocks": "숨겨진 도메인",
@@ -280,7 +281,7 @@
   "status.cancel_reblog_private": "부스트 취소",
   "status.cannot_reblog": "이 포스트는 부스트 할 수 없습니다",
   "status.delete": "삭제",
-  "status.detailed_status": "Detailed conversation view",
+  "status.detailed_status": "대화 자세히 보기",
   "status.direct": "@{name}에게 다이렉트 메시지",
   "status.embed": "공유하기",
   "status.favourite": "즐겨찾기",
@@ -297,7 +298,7 @@
   "status.reblog": "부스트",
   "status.reblog_private": "원래의 수신자들에게 부스트",
   "status.reblogged_by": "{name}님이 부스트 했습니다",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
+  "status.reblogs.empty": "아직 아무도 이 툿을 부스트하지 않았습니다. 부스트 한 사람들이 여기에 표시 됩니다.",
   "status.redraft": "지우고 다시 쓰기",
   "status.reply": "답장",
   "status.replyAll": "전원에게 답장",
@@ -319,7 +320,7 @@
   "trends.count_by_accounts": "{count} {rawCount, plural, one {명} other {명}} 의 사람들이 말하고 있습니다",
   "ui.beforeunload": "지금 나가면 저장되지 않은 항목을 잃게 됩니다.",
   "upload_area.title": "드래그 & 드롭으로 업로드",
-  "upload_button.label": "미디어 추가",
+  "upload_button.label": "미디어 추가 (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_form.description": "시각장애인을 위한 설명",
   "upload_form.focus": "크롭",
   "upload_form.undo": "삭제",
diff --git a/app/javascript/mastodon/locales/locale-data/co.js b/app/javascript/mastodon/locales/locale-data/co.js
new file mode 100644
index 000000000..2b071ecbd
--- /dev/null
+++ b/app/javascript/mastodon/locales/locale-data/co.js
@@ -0,0 +1,108 @@
+/*eslint eqeqeq: "off"*/
+/*eslint no-nested-ternary: "off"*/
+/*eslint quotes: "off"*/
+
+export default [{
+  locale: "co",
+  pluralRuleFunction: function (e, a) {
+    return a ? 1 == e ? "one" : "other" : e >= 0 && e < 2 ? "one" : "other";
+  },
+  fields: {
+    year: {
+      displayName: "annu",
+      relative: {
+        0: "quist'annu",
+        1: "l'annu chì vene",
+        "-1": "l'annu passatu",
+      },
+      relativeTime: {
+        future: {
+          one: "in {0} annu",
+          other: "in {0} anni",
+        },
+        past: {
+          one: "{0} annu fà",
+          other: "{0} anni fà",
+        },
+      },
+    },
+    month: {
+      displayName: "mese",
+      relative: {
+        0: "Questu mese",
+        1: "u mese chì vene",
+        "-1": "u mese passatu",
+      },
+      relativeTime: {
+        future: {
+          one: "in {0} mese",
+          other: "in {0} mesi",
+        },
+        past: {
+          one: "{0} mese fà",
+          other: "{0} mesi fà",
+        },
+      },
+    },
+    day: {
+      displayName: "ghjornu",
+      relative: {
+        0: "oghje",
+        1: "dumane",
+        "-1": "eri",
+      },
+      relativeTime: {
+        future: {
+          one: "in {0} ghjornu",
+          other: "in {0} ghjornu",
+        },
+        past: {
+          one: "{0} ghjornu fà",
+          other: "{0} ghjorni fà",
+        },
+      },
+    },
+    hour: {
+      displayName: "ora",
+      relativeTime: {
+        future: {
+          one: "in {0} ora",
+          other: "in {0} ore",
+        },
+        past: {
+          one: "{0} ora fà",
+          other: "{0} ore fà",
+        },
+      },
+    },
+    minute: {
+      displayName: "minuta",
+      relativeTime: {
+        future: {
+          one: "in {0} minuta",
+          other: "in {0} minute",
+        },
+        past: {
+          one: "{0} minuta fà",
+          other: "{0} minute fà",
+        },
+      },
+    },
+    second: {
+      displayName: "siconda",
+      relative: {
+        0: "avà",
+      },
+      relativeTime: {
+        future: {
+          one: "in {0} siconda",
+          other: "in {0} siconde",
+        },
+        past: {
+          one: "{0} siconda fà",
+          other: "{0} siconde fà",
+        },
+      },
+    },
+  },
+}];
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index 2ad2a6928..f829146f1 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -1,7 +1,7 @@
 {
   "account.badges.bot": "Bot",
   "account.block": "Blokkeer @{name}",
-  "account.block_domain": "Negeer alles van {domain}",
+  "account.block_domain": "Verberg alles van {domain}",
   "account.blocked": "Geblokkeerd",
   "account.direct": "Direct Message @{name}",
   "account.disclaimer_full": "De informatie hieronder kan mogelijk een incompleet beeld geven van dit gebruikersprofiel.",
@@ -10,11 +10,12 @@
   "account.endorse": "Op profiel weergeven",
   "account.follow": "Volgen",
   "account.followers": "Volgers",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "Niemand volgt nog deze gebruiker.",
   "account.follows": "Volgt",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "Deze gebruiker volgt nog niemand.",
   "account.follows_you": "Volgt jou",
   "account.hide_reblogs": "Verberg boosts van @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Media",
   "account.mention": "Vermeld @{name}",
   "account.moved_to": "{name} is verhuisd naar:",
@@ -28,7 +29,7 @@
   "account.share": "Profiel van @{name} delen",
   "account.show_reblogs": "Toon boosts van @{name}",
   "account.unblock": "Deblokkeer @{name}",
-  "account.unblock_domain": "{domain} niet langer negeren",
+  "account.unblock_domain": "{domain} niet langer verbergen",
   "account.unendorse": "Niet op profiel weergeven",
   "account.unfollow": "Ontvolgen",
   "account.unmute": "@{name} niet langer negeren",
@@ -46,7 +47,7 @@
   "column.blocks": "Geblokkeerde gebruikers",
   "column.community": "Lokale tijdlijn",
   "column.direct": "Directe berichten",
-  "column.domain_blocks": "Verborgen domeinen",
+  "column.domain_blocks": "Genegeerde servers",
   "column.favourites": "Favorieten",
   "column.follow_requests": "Volgverzoeken",
   "column.home": "Start",
@@ -84,12 +85,12 @@
   "confirmations.delete.message": "Weet je het zeker dat je deze toot wilt verwijderen?",
   "confirmations.delete_list.confirm": "Verwijderen",
   "confirmations.delete_list.message": "Weet je zeker dat je deze lijst definitief wilt verwijderen?",
-  "confirmations.domain_block.confirm": "Negeer alles van deze server",
+  "confirmations.domain_block.confirm": "Verberg alles van deze server",
   "confirmations.domain_block.message": "Weet je het echt heel erg zeker dat je alles van {domain} wilt negeren? In de meeste gevallen is het blokkeren of negeren van een paar specifieke personen voldoende en beter. Je zult geen toots van deze server op openbare tijdlijnen zien of in jouw meldingen. Jouw volgers van deze server worden verwijderd.",
   "confirmations.mute.confirm": "Negeren",
   "confirmations.mute.message": "Weet je het zeker dat je {name} wilt negeren?",
   "confirmations.redraft.confirm": "Verwijderen en herschrijven",
-  "confirmations.redraft.message": "Weet je zeker dat je deze toot wilt verwijderen en herschrijven? Je verliest wel alle reacties, boosts en favorieten.",
+  "confirmations.redraft.message": "Weet je zeker dat je deze toot wilt verwijderen en herschrijven? Je verliest wel de boosts en favorieten, en reacties op de originele toot zitten niet meer aan de nieuwe toot vast.",
   "confirmations.unfollow.confirm": "Ontvolgen",
   "confirmations.unfollow.message": "Weet je het zeker dat je {name} wilt ontvolgen?",
   "embed.instructions": "Embed deze toot op jouw website, door de onderstaande code te kopiëren.",
@@ -108,19 +109,19 @@
   "emoji_button.search_results": "Zoekresultaten",
   "emoji_button.symbols": "Symbolen",
   "emoji_button.travel": "Reizen en plekken",
-  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.blocks": "Jij hebt nog geen enkele gebruiker geblokkeerd.",
   "empty_column.community": "De lokale tijdlijn is nog leeg. Toot iets in het openbaar om de bal aan het rollen te krijgen!",
   "empty_column.direct": "Je hebt nog geen directe berichten. Wanneer je er een verzend of ontvangt, zijn deze hier te zien.",
-  "empty_column.domain_blocks": "There are no hidden 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.domain_blocks": "Er zijn nog geen genegeerde domeinen.",
+  "empty_column.favourited_statuses": "Jij hebt nog geen favoriete toots. Wanneer je er een als favoriet markeert, valt deze hier te zien.",
+  "empty_column.favourites": "Niemand heeft nog deze toot als favoriet gemarkeerd. Wanneer iemand dit doet, valt dat hier te zien.",
+  "empty_column.follow_requests": "Jij hebt nog enkel volgverzoek ontvangen. Wanneer je er eentje ontvangt, valt dat hier te zien.",
   "empty_column.hashtag": "Er is nog niks te vinden onder deze hashtag.",
   "empty_column.home": "Jij volgt nog niemand. Bezoek {public} of gebruik het zoekvenster om andere mensen te ontmoeten.",
   "empty_column.home.public_timeline": "de globale tijdlijn",
   "empty_column.list": "Er is nog niks in deze lijst. Wanneer lijstleden nieuwe toots publiceren, zijn deze hier te zien.",
-  "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.lists": "Jij hebt nog enkele lijst. Wanneer je er eentje hebt aangemaakt, valt deze hier te zien.",
+  "empty_column.mutes": "Jij hebt nog geen gebruikers genegeerd.",
   "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",
   "follow_request.authorize": "Goedkeuren",
@@ -137,32 +138,32 @@
   "home.column_settings.show_reblogs": "Boosts tonen",
   "home.column_settings.show_replies": "Reacties tonen",
   "keyboard_shortcuts.back": "om terug te gaan",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "om de door jou geblokkeerde gebruikers te tonen",
   "keyboard_shortcuts.boost": "om te boosten",
   "keyboard_shortcuts.column": "om op een toot te focussen in één van de kolommen",
   "keyboard_shortcuts.compose": "om het tekstvak voor toots te focussen",
   "keyboard_shortcuts.description": "Omschrijving",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "om jouw directe berichten te tonen",
   "keyboard_shortcuts.down": "om naar beneden door de lijst te bewegen",
   "keyboard_shortcuts.enter": "om toot volledig te tonen",
   "keyboard_shortcuts.favourite": "om als favoriet te markeren",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "om jouw lijst met favorieten te tonen",
+  "keyboard_shortcuts.federated": "om de globale tijdlijn te tonen",
   "keyboard_shortcuts.heading": "Sneltoetsen",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "om jouw starttijdlijn te tonen",
   "keyboard_shortcuts.hotkey": "Sneltoets",
   "keyboard_shortcuts.legend": "om deze legenda te tonen",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "om de lokale tijdlijn te tonen",
   "keyboard_shortcuts.mention": "om de auteur te vermelden",
-  "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.pinned": "to open pinned toots list",
+  "keyboard_shortcuts.muted": "om de door jou genegeerde gebruikers te tonen",
+  "keyboard_shortcuts.my_profile": "om jouw profiel te tonen",
+  "keyboard_shortcuts.notifications": "om jouw meldingen te tonen",
+  "keyboard_shortcuts.pinned": "om jouw vastgezette toots te tonen",
   "keyboard_shortcuts.profile": "om het gebruikersprofiel te openen",
   "keyboard_shortcuts.reply": "om te reageren",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "om jouw volgverzoeken te tonen",
   "keyboard_shortcuts.search": "om het zoekvak te focussen",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
+  "keyboard_shortcuts.start": "om de \"Aan de slag\"-kolom te tonen",
   "keyboard_shortcuts.toggle_hidden": "om tekst achter een waarschuwing (CW) te tonen/verbergen",
   "keyboard_shortcuts.toot": "om een nieuwe toot te starten",
   "keyboard_shortcuts.unfocus": "om het tekst- en zoekvak te ontfocussen",
@@ -183,16 +184,16 @@
   "missing_indicator.label": "Niet gevonden",
   "missing_indicator.sublabel": "Deze hulpbron kan niet gevonden worden",
   "mute_modal.hide_notifications": "Verberg meldingen van deze persoon?",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "Mobiele apps",
   "navigation_bar.blocks": "Geblokkeerde gebruikers",
   "navigation_bar.community_timeline": "Lokale tijdlijn",
-  "navigation_bar.compose": "Compose new toot",
+  "navigation_bar.compose": "Nieuw toot schrijven",
   "navigation_bar.direct": "Directe berichten",
   "navigation_bar.discover": "Ontdekken",
-  "navigation_bar.domain_blocks": "Verborgen domeinen",
+  "navigation_bar.domain_blocks": "Genegeerde domeinen",
   "navigation_bar.edit_profile": "Profiel bewerken",
   "navigation_bar.favourites": "Favorieten",
-  "navigation_bar.filters": "Genegeerde woorden",
+  "navigation_bar.filters": "Filters",
   "navigation_bar.follow_requests": "Volgverzoeken",
   "navigation_bar.info": "Over deze server",
   "navigation_bar.keyboard_shortcuts": "Sneltoetsen",
@@ -259,8 +260,8 @@
   "relative_time.seconds": "{number}s",
   "reply_indicator.cancel": "Annuleren",
   "report.forward": "Doorsturen naar {target}",
-  "report.forward_hint": "Het account bevindt zich op een andere server. Stuur daar eveneens een geanonimiseerde kopie van de gerapporteerde toot(s) naartoe?",
-  "report.hint": "De gerapporteerde toot(s) worden naar de moderatoren van  jouw server gestuurd. Je kunt hieronder een uitleg geven waarom je dit account rapporteert:",
+  "report.forward_hint": "Het account bevindt zich op een andere server. Stuur daar eveneens een geanonimiseerde kopie van de rapportage naartoe?",
+  "report.hint": "De rapportage wordt naar de moderatoren van jouw server gestuurd. Je kunt hieronder een uitleg geven waarom je dit account rapporteert:",
   "report.placeholder": "Extra opmerkingen",
   "report.submit": "Verzenden",
   "report.target": "Rapporteer {target}",
@@ -280,7 +281,7 @@
   "status.cancel_reblog_private": "Niet langer boosten",
   "status.cannot_reblog": "Deze toot kan niet geboost worden",
   "status.delete": "Verwijderen",
-  "status.detailed_status": "Detailed conversation view",
+  "status.detailed_status": "Uitgebreide gespreksweergave",
   "status.direct": "Directe toot @{name}",
   "status.embed": "Embed",
   "status.favourite": "Favoriet",
@@ -297,7 +298,7 @@
   "status.reblog": "Boost",
   "status.reblog_private": "Boost naar oorspronkelijke ontvangers",
   "status.reblogged_by": "{name} boostte",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
+  "status.reblogs.empty": "Niemand heeft deze toot nog geboost. Wanneer iemand dit doet, valt dat hier te zien.",
   "status.redraft": "Verwijderen en herschrijven",
   "status.reply": "Reageren",
   "status.replyAll": "Reageer op iedereen",
@@ -319,7 +320,7 @@
   "trends.count_by_accounts": "{count} {rawCount, plural, one {persoon praat} other {mensen praten}} hierover",
   "ui.beforeunload": "Je concept zal verloren gaan als je Mastodon verlaat.",
   "upload_area.title": "Hierin slepen om te uploaden",
-  "upload_button.label": "Media toevoegen",
+  "upload_button.label": "Media toevoegen (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_form.description": "Omschrijf dit voor mensen met een visuele beperking",
   "upload_form.focus": "Bijsnijden",
   "upload_form.undo": "Verwijderen",
diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json
index d533ac315..e531789a9 100644
--- a/app/javascript/mastodon/locales/no.json
+++ b/app/javascript/mastodon/locales/no.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "Følger deg",
   "account.hide_reblogs": "Skjul fremhevinger fra @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Media",
   "account.mention": "Nevn @{name}",
   "account.moved_to": "{name} har flyttet til:",
diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index 06fa058db..182710038 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -10,11 +10,12 @@
   "account.endorse": "Mostrar pel perfil",
   "account.follow": "Sègre",
   "account.followers": "Seguidors",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "Degun sèc pas aqueste utilizaire pel moment.",
   "account.follows": "Abonaments",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "Aqueste utilizaire sèc pas degun pel moment.",
   "account.follows_you": "Vos sèc",
   "account.hide_reblogs": "Rescondre los partatges de @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Mèdias",
   "account.mention": "Mencionar @{name}",
   "account.moved_to": "{name} a mudat los catons a :",
@@ -89,7 +90,7 @@
   "confirmations.mute.confirm": "Rescondre",
   "confirmations.mute.message": "Volètz vertadièrament rescondre {name} ?",
   "confirmations.redraft.confirm": "Escafar & tornar formular",
-  "confirmations.redraft.message": "Volètz vertadièrament escafar aqueste estatut e lo reformular ? Perdretz totas sas responsas, sos partiments e favorits.",
+  "confirmations.redraft.message": "Volètz vertadièrament escafar aqueste estatut e lo reformular ? Tote sos partiments e favorits seràn perduts, e sas responsas seràn orfanèlas.",
   "confirmations.unfollow.confirm": "Quitar de sègre",
   "confirmations.unfollow.message": "Volètz vertadièrament quitar de sègre {name} ?",
   "embed.instructions": "Embarcar aqueste estatut per lo far veire sus un site Internet en copiar lo còdi çai-jos.",
@@ -108,19 +109,19 @@
   "emoji_button.search_results": "Resultats de recèrca",
   "emoji_button.symbols": "Simbòls",
   "emoji_button.travel": "Viatges & lòcs",
-  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.blocks": "Avètz pas blocat degun pel moment.",
   "empty_column.community": "Lo flux public local es void. Escrivètz quicòm per lo garnir !",
   "empty_column.direct": "Avètz pas encara cap de messatges. Quand ne mandatz un o que ne recebètz un, serà mostrat aquí.",
-  "empty_column.domain_blocks": "There are no hidden 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.domain_blocks": "I a pas encara cap de domeni amagat.",
+  "empty_column.favourited_statuses": "Avètz pas encara cap de tut favorit. Quanda n’auretz un, apareisserà  aquí.",
+  "empty_column.favourites": "Degun a pas encara mes en favorit aqueste tut. Quanda qualqu’un o farà, apareisserà  aquí.",
+  "empty_column.follow_requests": "Avètz pas encara de demanda d’abonament. Quand n’auretz una apareisserà aquí.",
   "empty_column.hashtag": "I a pas encara de contengut ligat a aquesta etiqueta.",
   "empty_column.home": "Vòstre flux d’acuèlh es void. Visitatz {public} o utilizatz la recèrca per vos connectar a d’autras personas.",
   "empty_column.home.public_timeline": "lo flux public",
   "empty_column.list": "I a pas res dins la lista pel moment. Quand de membres d’aquesta lista publiquen de novèls estatuts los veiretz aquí.",
-  "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.lists": "Encara avètz pas cap de lista. Quand ne creetz una, apareisserà aquí.",
+  "empty_column.mutes": "Encara avètz pas mes en silenci degun.",
   "empty_column.notifications": "Avètz pas encara de notificacions. Respondètz a qualqu’un per començar una conversacion.",
   "empty_column.public": "I a pas res aquí ! Escrivètz quicòm de public, o seguètz de personas d’autras instàncias per garnir lo flux public",
   "follow_request.authorize": "Acceptar",
@@ -137,32 +138,32 @@
   "home.column_settings.show_reblogs": "Mostrar los partatges",
   "home.column_settings.show_replies": "Mostrar las responsas",
   "keyboard_shortcuts.back": "anar enrèire",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "per dobrir la lista d’utilizaires blocats",
   "keyboard_shortcuts.boost": "partejar",
   "keyboard_shortcuts.column": "centrar un estatut a una colomna",
   "keyboard_shortcuts.compose": "anar al camp tèxte",
   "keyboard_shortcuts.description": "Descripcion",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "per dobrir la columna de messatges dirèctes",
   "keyboard_shortcuts.down": "far davalar dins la lista",
   "keyboard_shortcuts.enter": "dobrir los estatuts",
   "keyboard_shortcuts.favourite": "apondre als favorits",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "per dobrir la lista de favorits",
+  "keyboard_shortcuts.federated": "per dobrir lo flux public global",
   "keyboard_shortcuts.heading": "Acorchis clavièr",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "per dobrir lo flux public local",
   "keyboard_shortcuts.hotkey": "Acorchis",
   "keyboard_shortcuts.legend": "mostrar aquesta legenda",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "per dobrir lo flux public local",
   "keyboard_shortcuts.mention": "mencionar l’autor",
-  "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.pinned": "to open pinned toots list",
+  "keyboard_shortcuts.muted": "per dorbir la lista dels utilizaires silenciats",
+  "keyboard_shortcuts.my_profile": "per dobrir vòstre perfil",
+  "keyboard_shortcuts.notifications": "per dobrir la columna de notificacions",
+  "keyboard_shortcuts.pinned": "per dobrir la lista dels tuts penjats",
   "keyboard_shortcuts.profile": "per dobrir lo perfil de l’autor",
   "keyboard_shortcuts.reply": "respondre",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "per dorbir la lista de demanda d’abonament",
   "keyboard_shortcuts.search": "anar a la recèrca",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
+  "keyboard_shortcuts.start": "per dobrir la columna «Per començar»",
   "keyboard_shortcuts.toggle_hidden": "mostrar/amagar lo tèxte dels avertiments",
   "keyboard_shortcuts.toot": "començar un estatut tot novèl",
   "keyboard_shortcuts.unfocus": "quitar lo camp tèxte/de recèrca",
@@ -186,7 +187,7 @@
   "navigation_bar.apps": "Aplicacions mobil",
   "navigation_bar.blocks": "Personas blocadas",
   "navigation_bar.community_timeline": "Flux public local",
-  "navigation_bar.compose": "Compose new toot",
+  "navigation_bar.compose": "Escriure un nòu tut",
   "navigation_bar.direct": "Messatges dirèctes",
   "navigation_bar.discover": "Trobar",
   "navigation_bar.domain_blocks": "Domenis resconduts",
@@ -280,7 +281,7 @@
   "status.cancel_reblog_private": "Quitar de partejar",
   "status.cannot_reblog": "Aqueste estatut pòt pas èsser partejat",
   "status.delete": "Escafar",
-  "status.detailed_status": "Detailed conversation view",
+  "status.detailed_status": "Vista detalhada de la convèrsa",
   "status.direct": "Messatge per @{name}",
   "status.embed": "Embarcar",
   "status.favourite": "Apondre als favorits",
@@ -297,7 +298,7 @@
   "status.reblog": "Partejar",
   "status.reblog_private": "Partejar a l’audiéncia d’origina",
   "status.reblogged_by": "{name} a partejat",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
+  "status.reblogs.empty": "Degun a pas encara partejat aqueste tut. Quand qualqu’un o farà, apareisserà aquí.",
   "status.redraft": "Escafar e tornar formular",
   "status.reply": "Respondre",
   "status.replyAll": "Respondre a la conversacion",
@@ -319,7 +320,7 @@
   "trends.count_by_accounts": "{count} {rawCount, plural, one {person} ne charra other {people}} ne charran",
   "ui.beforeunload": "Vòstre brolhon serà perdut se quitatz Mastodon.",
   "upload_area.title": "Lisatz e depausatz per mandar",
-  "upload_button.label": "Ajustar un mèdia",
+  "upload_button.label": "Ajustar un mèdia (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_form.description": "Descripcion pels mal vesents",
   "upload_form.focus": "Retalhar",
   "upload_form.undo": "Suprimir",
diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json
index 0ad3499bc..aa2535bf7 100644
--- a/app/javascript/mastodon/locales/pl.json
+++ b/app/javascript/mastodon/locales/pl.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "Ten użytkownik nie śledzi jeszcze nikogo.",
   "account.follows_you": "Śledzi Cię",
   "account.hide_reblogs": "Ukryj podbicia od @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Zawartość multimedialna",
   "account.mention": "Wspomnij o @{name}",
   "account.moved_to": "{name} przeniósł(-osła) się do:",
@@ -324,7 +325,7 @@
   "trends.count_by_accounts": "{count} {rawCount, plural, one {osoba rozmawia} few {osoby rozmawiają} other {osób rozmawia}} o tym",
   "ui.beforeunload": "Utracisz tworzony wpis, jeżeli opuścisz Mastodona.",
   "upload_area.title": "Przeciągnij i upuść aby wysłać",
-  "upload_button.label": "Dodaj zawartość multimedialną (JPEG, PNG, GIF, WebM, MP4)",
+  "upload_button.label": "Dodaj zawartość multimedialną (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_form.description": "Wprowadź opis dla niewidomych i niedowidzących",
   "upload_form.focus": "Przytnij",
   "upload_form.undo": "Usuń",
diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json
index ed6ea3674..9c48f0bc1 100644
--- a/app/javascript/mastodon/locales/pt-BR.json
+++ b/app/javascript/mastodon/locales/pt-BR.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "Segue você",
   "account.hide_reblogs": "Esconder compartilhamentos de @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Mídia",
   "account.mention": "Mencionar @{name}",
   "account.moved_to": "{name} se mudou para:",
diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json
index 2f601cb32..096fded95 100644
--- a/app/javascript/mastodon/locales/pt.json
+++ b/app/javascript/mastodon/locales/pt.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "É teu seguidor",
   "account.hide_reblogs": "Esconder partilhas de @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Media",
   "account.mention": "Mencionar @{name}",
   "account.moved_to": "{name} mudou a sua conta para:",
diff --git a/app/javascript/mastodon/locales/ro.json b/app/javascript/mastodon/locales/ro.json
new file mode 100644
index 000000000..fda82136e
--- /dev/null
+++ b/app/javascript/mastodon/locales/ro.json
@@ -0,0 +1,337 @@
+{
+  "account.badges.bot": "Bot",
+  "account.block": "Blochează @{name}",
+  "account.block_domain": "Ascunde tot de la {domain}",
+  "account.blocked": "Blocat",
+  "account.direct": "Mesaj direct @{name}",
+  "account.disclaimer_full": "Informațiile de mai jos pot reflecta profilul incomplet al utilizatorului.",
+  "account.domain_blocked": "Domeniu ascuns",
+  "account.edit_profile": "Editează profilul",
+  "account.endorse": "Redistribuie pe profil",
+  "account.follow": "Urmărește",
+  "account.followers": "Urmăritori",
+  "account.followers.empty": "Nimeni nu urmărește acest utilizator incă.",
+  "account.follows": "Urmărește",
+  "account.follows.empty": "Acest utilizator nu urmărește pe nimeni incă.",
+  "account.follows_you": "Te urmărește",
+  "account.hide_reblogs": "Ascunde redistribuirile de la @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.media": "Media",
+  "account.mention": "Menționează @{name}",
+  "account.moved_to": "{name} a fost mutat la:",
+  "account.mute": "Oprește @{name}",
+  "account.mute_notifications": "Oprește notificările de la @{name}",
+  "account.muted": "Oprit",
+  "account.posts": "Postări",
+  "account.posts_with_replies": "Postări și replici",
+  "account.report": "Raportează @{name}",
+  "account.requested": "Așteaptă aprobare. Apasă pentru a anula cererea de urmărire",
+  "account.share": "Distribuie profilul lui @{name}",
+  "account.show_reblogs": "Arată redistribuirile de la @{name}",
+  "account.unblock": "Deblochează @{name}",
+  "account.unblock_domain": "Arată {domain}",
+  "account.unendorse": "Nu promova pe profil",
+  "account.unfollow": "Nu mai urmări",
+  "account.unmute": "Pornește notificările @{name}",
+  "account.unmute_notifications": "Pornește notificările de la @{name}",
+  "account.view_full_profile": "Vezi profilul complet",
+  "alert.unexpected.message": "A apărut o eroare neașteptată.",
+  "alert.unexpected.title": "Hopa!",
+  "boost_modal.combo": "Poți apăsa {combo} pentru a sări peste asta data viitoare",
+  "bundle_column_error.body": "Ceva nu a funcționat la încărcarea acestui component.",
+  "bundle_column_error.retry": "Încearcă din nou",
+  "bundle_column_error.title": "Eoare de rețea",
+  "bundle_modal_error.close": "Închide",
+  "bundle_modal_error.message": "Ceva nu a funcționat în timupul încărcării acestui component.",
+  "bundle_modal_error.retry": "Încearcă din nou",
+  "column.blocks": "Utilizatori blocați",
+  "column.community": "Fluxul Local",
+  "column.direct": "Mesaje directe",
+  "column.domain_blocks": "Domenii ascunse",
+  "column.favourites": "Favorite",
+  "column.follow_requests": "Cereri de urmărire",
+  "column.home": "Acasă",
+  "column.lists": "Liste",
+  "column.mutes": "Utilizatori opriți",
+  "column.notifications": "Notificări",
+  "column.pins": "Postări fixate",
+  "column.public": "Flux global",
+  "column_back_button.label": "Înapoi",
+  "column_header.hide_settings": "Ascunde setările",
+  "column_header.moveLeft_settings": "Mută coloana la stânga",
+  "column_header.moveRight_settings": "Mută coloana la dreapta",
+  "column_header.pin": "Fixează",
+  "column_header.show_settings": "Arată setările",
+  "column_header.unpin": "Eliberează",
+  "column_subheading.settings": "Setări",
+  "community.column_settings.media_only": "Doar media",
+  "compose_form.direct_message_warning": "Această postare va fi trimisă doar utilizatorilor menționați.",
+  "compose_form.direct_message_warning_learn_more": "Află mai multe",
+  "compose_form.hashtag_warning": "Această postare nu va fi listată sub nici un hastag. Doar postările publice pot fi găsite dupa un hastag.",
+  "compose_form.lock_disclaimer": "Contul tău nu este {locked}. Toată lumea te poate urmări pentru a vedea postările doar pentru urmăritori.",
+  "compose_form.lock_disclaimer.lock": "privat",
+  "compose_form.placeholder": "La ce te gândești?",
+  "compose_form.publish": "Postează",
+  "compose_form.publish_loud": "{publish}!",
+  "compose_form.sensitive.marked": "Conținutul media este marcat ca sensibil",
+  "compose_form.sensitive.unmarked": "Conținutul media nu este marcat ca sensibil",
+  "compose_form.spoiler.marked": "Textul este ascuns sub advertizare",
+  "compose_form.spoiler.unmarked": "Textul nu este ascuns",
+  "compose_form.spoiler_placeholder": "Scrie adveritzarea aici",
+  "confirmation_modal.cancel": "Anulează",
+  "confirmations.block.confirm": "Blochează",
+  "confirmations.block.message": "Ești sigur că vrei să blochezi {name}?",
+  "confirmations.delete.confirm": "Șterge",
+  "confirmations.delete.message": "Ești șigur că vrei să ștergi asta?",
+  "confirmations.delete_list.confirm": "Șterge",
+  "confirmations.delete_list.message": "Ești sigur că vrei să ștergi permanent această listă?",
+  "confirmations.domain_block.confirm": "Ascunde tot domeniul",
+  "confirmations.domain_block.message": "Ești absolut sigur că vrei să blochezi complet {domain}? În cele mai multe cazuri raportarea sau oprirea anumitor lucruri este suficientă și de preferat. Nu vei mai vedea nici un conținut de la acest domeniu in nici un flux public sau în notificările tale. Urmăritorii tăi de la acele domenii vor fi retrași.",
+  "confirmations.mute.confirm": "Oprește",
+  "confirmations.mute.message": "Ești sigur că vrei să oprești {name}?",
+  "confirmations.redraft.confirm": "Șterge și salvează ca ciornă",
+  "confirmations.redraft.message": "Ești sigur că vrei să faci asta? Tot ce ține de această postare, inclusiv răspunsurile vor fi deconectate.",
+  "confirmations.unfollow.confirm": "Nu mai urmări",
+  "confirmations.unfollow.message": "Ești sigur că nu mai vrei să îl urmărești pe {name}?",
+  "embed.instructions": "Inserează această postare pe site-ul tău adăugând codul de mai jos.",
+  "embed.preview": "Cam așa va arăta:",
+  "emoji_button.activity": "Activitate",
+  "emoji_button.custom": "Personalizat",
+  "emoji_button.flags": "Marcaje",
+  "emoji_button.food": "Mâncare și Băuturi",
+  "emoji_button.label": "Inserează un emoji",
+  "emoji_button.nature": "Natură",
+  "emoji_button.not_found": "Fară emojiuri (╯°□°)╯︵ ┻━┻",
+  "emoji_button.objects": "Obiecte",
+  "emoji_button.people": "Persoane",
+  "emoji_button.recent": "Utilizate frecvent",
+  "emoji_button.search": "Caută...",
+  "emoji_button.search_results": "Rezultatele căutării",
+  "emoji_button.symbols": "Simboluri",
+  "emoji_button.travel": "Călătorii si Locuri",
+  "empty_column.blocks": "Nu ai blocat nici un utilizator incă.",
+  "empty_column.community": "Fluxul local este gol. Scrie ceva public pentru a împinge bila la vale!",
+  "empty_column.direct": "Nu ai nici un mesaj direct incă. Când trimiți sau primești unul, vor fi afișate aici.",
+  "empty_column.domain_blocks": "Nu sunt domenii ascunse incă.",
+  "empty_column.favourited_statuses": "Nu ai nici o postare favorită încă. Când vei avea, vor fi afișate aici.",
+  "empty_column.favourites": "Nimeni nu are această postare adăugată la favorite. Când cineva o va face va fi afișat aici.",
+  "empty_column.follow_requests": "Nu ai încă nici o cerere de urmărire. Când vei primi una, va fi afișată aici.",
+  "empty_column.hashtag": "Acest hastag nu a fost folosit încă nicăieri.",
+  "empty_column.home": "Fluxul tău este gol. Vizitează {public} sau fă o căutare pentru a începe să cunoști oameni noi.",
+  "empty_column.home.public_timeline": "fluxul public",
+  "empty_column.list": "Nu este nimic încă în această listă. Când membrii acestei liste vor începe să posteze, va apărea aici.",
+  "empty_column.lists": "Nu ai încă nici o listă. Când vei crea una, va apărea aici.",
+  "empty_column.mutes": "Nu ai oprit nici un utilizator incă.",
+  "empty_column.notifications": "Nu ai nici o notificare încă. Interacționează cu alții pentru a începe o conversație.",
+  "empty_column.public": "Nu este nimci aici încă! Scrie ceva public, sau urmărește alți utilizatori din alte instanțe pentru a porni fluxul",
+  "follow_request.authorize": "Autorizează",
+  "follow_request.reject": "Respinge",
+  "getting_started.developers": "Dezvoltatori",
+  "getting_started.documentation": "Documentație",
+  "getting_started.find_friends": "Importă din Twitter",
+  "getting_started.heading": "Începe",
+  "getting_started.invite": "Invită oameni",
+  "getting_started.open_source_notice": "Mastodon este o rețea de socializare de tip open source. Puteți contribuii la dezvoltarea ei sau să semnalați erorile pe GitHub la {github}.",
+  "getting_started.security": "Securitate",
+  "getting_started.terms": "Termenii de Utilizare",
+  "home.column_settings.basic": "De bază",
+  "home.column_settings.show_reblogs": "Arată redistribuirile",
+  "home.column_settings.show_replies": "Arată răspunsurile",
+  "keyboard_shortcuts.back": "navighează inapoi",
+  "keyboard_shortcuts.blocked": "să deschidă lista utilizatorilor blocați",
+  "keyboard_shortcuts.boost": "să redistribuie",
+  "keyboard_shortcuts.column": "să focuzeze o postare in una dintre coloane",
+  "keyboard_shortcuts.compose": "sa focuzeze zona de compunere",
+  "keyboard_shortcuts.description": "Descriere",
+  "keyboard_shortcuts.direct": "sa deschida coloane de mesaje directe",
+  "keyboard_shortcuts.down": "sa fie mutata jos in lista",
+  "keyboard_shortcuts.enter": "sa deschisa status",
+  "keyboard_shortcuts.favourite": "sa adauge la favorite",
+  "keyboard_shortcuts.favourites": "sa deschida lista cu favorite",
+  "keyboard_shortcuts.federated": "sa deschida fluxul global",
+  "keyboard_shortcuts.heading": "Comenzi din tastatură",
+  "keyboard_shortcuts.home": "sa deschida fluxul principal",
+  "keyboard_shortcuts.hotkey": "Prescurtări",
+  "keyboard_shortcuts.legend": "sa afiseze aceasta legenda",
+  "keyboard_shortcuts.local": "sa deschida fluxul local",
+  "keyboard_shortcuts.mention": "sa mentioneze autorul",
+  "keyboard_shortcuts.muted": "sa deschida lista utilizatorilor opriti",
+  "keyboard_shortcuts.my_profile": "sa deschida profilul tau",
+  "keyboard_shortcuts.notifications": "sa deschida coloana cu notificari",
+  "keyboard_shortcuts.pinned": "sa deschida lista postarilor fixate",
+  "keyboard_shortcuts.profile": "sa deschida porfilul autorului",
+  "keyboard_shortcuts.reply": "sa raspunda",
+  "keyboard_shortcuts.requests": "sa deschida lista cu cereri de urmarire",
+  "keyboard_shortcuts.search": "sa focuseze cautarea",
+  "keyboard_shortcuts.start": "sa deschida coloana \"Incepere\"",
+  "keyboard_shortcuts.toggle_hidden": "sa arate/ascunda textul in spatele CW",
+  "keyboard_shortcuts.toot": "sa inceapa o noua postare",
+  "keyboard_shortcuts.unfocus": "sa dezactiveze compunerea/cautarea",
+  "keyboard_shortcuts.up": "sa mute mai sus in lista",
+  "lightbox.close": "Închide",
+  "lightbox.next": "Următorul",
+  "lightbox.previous": "Precedentul",
+  "lists.account.add": "Adaugă în listă",
+  "lists.account.remove": "Elimină din listă",
+  "lists.delete": "Șterge listă",
+  "lists.edit": "Editează lista",
+  "lists.new.create": "Adaugă listă",
+  "lists.new.title_placeholder": "Titlu pentru noua listă",
+  "lists.search": "Caută printre persoanale pe care le urmărești",
+  "lists.subheading": "Listele tale",
+  "loading_indicator.label": "Încărcare...",
+  "media_gallery.toggle_visible": "Comutați vizibilitatea",
+  "missing_indicator.label": "Nu a fost găsit",
+  "missing_indicator.sublabel": "Această resursă nu a putut fi găsită",
+  "mute_modal.hide_notifications": "Ascunzi notificările de la acest utilizator?",
+  "navigation_bar.apps": "Aplicații mobile",
+  "navigation_bar.blocks": "Utilizatori blocați",
+  "navigation_bar.community_timeline": "Flux local",
+  "navigation_bar.compose": "Compune o nouă postare",
+  "navigation_bar.direct": "Mesaje directe",
+  "navigation_bar.discover": "Descoperă",
+  "navigation_bar.domain_blocks": "Domenii ascunse",
+  "navigation_bar.edit_profile": "Editează profilul",
+  "navigation_bar.favourites": "Favorite",
+  "navigation_bar.filters": "Cuvinte oprite",
+  "navigation_bar.follow_requests": "Cereri de urmărire",
+  "navigation_bar.info": "Despre această instanță",
+  "navigation_bar.keyboard_shortcuts": "Prescurtări",
+  "navigation_bar.lists": "Liste",
+  "navigation_bar.logout": "Deconectare",
+  "navigation_bar.mutes": "Utilizatori opriți",
+  "navigation_bar.personal": "Personal",
+  "navigation_bar.pins": "Postări fixate",
+  "navigation_bar.preferences": "Preferințe",
+  "navigation_bar.public_timeline": "Flux global",
+  "navigation_bar.security": "Securitate",
+  "notification.favourite": "{name} a adăugat statusul tău la favorite",
+  "notification.follow": "{name} te urmărește",
+  "notification.mention": "{name} te-a menționat",
+  "notification.reblog": "{name} a redistribuit postarea ta",
+  "notifications.clear": "Șterge notificările",
+  "notifications.clear_confirmation": "Ești sigur că vrei să ștergi toate notificările?",
+  "notifications.column_settings.alert": "Notificări pe desktop",
+  "notifications.column_settings.favourite": "Favorite:",
+  "notifications.column_settings.follow": "Noi urmăritori:",
+  "notifications.column_settings.mention": "Mențiuni:",
+  "notifications.column_settings.push": "Notificări push",
+  "notifications.column_settings.push_meta": "Acest dispozitiv",
+  "notifications.column_settings.reblog": "Redistribuite:",
+  "notifications.column_settings.show": "Arată în coloană",
+  "notifications.column_settings.sound": "Redă sunet",
+  "notifications.group": "{count} notificări",
+  "onboarding.done": "Gata",
+  "onboarding.next": "Următorul",
+  "onboarding.page_five.public_timelines": "Fluxul local afișează postările publice de la toți utilizatorii pe {domain}. Fluxul global afișează postările publice de la toți utilizatorii din rețeaua mastodon pe care utilizatorii de pe {domain} ii urmăresc. Acestea sunt fluxurile publice, un mod grozav de a descoperi oameni noi.",
+  "onboarding.page_four.home": "Fluxul Acasă arată postarile celor pe care ii urmărești.",
+  "onboarding.page_four.notifications": "Coloana de notificări arată când cineva interacționează cu tine.",
+  "onboarding.page_one.federation": "Mastodon este o rețea independentă de servere care împreună formează o imensă retea de socializare. Noi numim aceste servere istanțe.",
+  "onboarding.page_one.full_handle": "Id-ul tău complet",
+  "onboarding.page_one.handle_hint": "Asta este ceea ce trebuie să le spuneți prietenilor când vor să vă caute.",
+  "onboarding.page_one.welcome": "Bun venit la Mastodon!",
+  "onboarding.page_six.admin": "Administatorul acestei instanțe este {admin}.",
+  "onboarding.page_six.almost_done": "Aproape gata...",
+  "onboarding.page_six.appetoot": "Distracție plăcută!",
+  "onboarding.page_six.apps_available": "Acestea sunt {apps} disponibile pentru iOS, Android și alte platforme.",
+  "onboarding.page_six.github": "Mastodon este un software de tip open source. Puteți raporta erori, cere facilități, sau să contribuiți pe {github}.",
+  "onboarding.page_six.guidelines": "regulamentul comunității",
+  "onboarding.page_six.read_guidelines": "Vă rugăm citiți {domain} {guidelines}!",
+  "onboarding.page_six.various_app": "aplicații mobile",
+  "onboarding.page_three.profile": "Editează profilul pentru a modifica fotografia de profil, descrierea si numele. Tot acolo vei găsi și alte preferințe.",
+  "onboarding.page_three.search": "Utilizează bara de căutare pentru a găsi oameni sau hastaguri precum {illustration} și {introductions}. Pentru a găsi o persoană care nu este înscrisă pe această instanță folosește id-ul lui complet.",
+  "onboarding.page_two.compose": "Scrie postări din coloana pentru compunere. Poți încărca imagini, schimba setările de confidențialitate, și adăuga advertisemente asupra conținutului.",
+  "onboarding.skip": "Omite",
+  "privacy.change": "Cine vede asta",
+  "privacy.direct.long": "Postează doar pentru utilizatorii menționați",
+  "privacy.direct.short": "Direct",
+  "privacy.private.long": "Postează doar pentru urmăritori",
+  "privacy.private.short": "Doar urmăritorii",
+  "privacy.public.long": "Postează în fluxul public",
+  "privacy.public.short": "Public",
+  "privacy.unlisted.long": "Nu afisa in fluxul public",
+  "privacy.unlisted.short": "Nelistat",
+  "regeneration_indicator.label": "Încărcare…",
+  "regeneration_indicator.sublabel": "Fluxul tău este în preparare!",
+  "relative_time.days": "{number}z",
+  "relative_time.hours": "{number}h",
+  "relative_time.just_now": "acum",
+  "relative_time.minutes": "{number}m",
+  "relative_time.seconds": "{number}s",
+  "reply_indicator.cancel": "Anulează",
+  "report.forward": "Redirecționează catre {target}",
+  "report.forward_hint": "Acest cont este de pe un alt server. Trimitem o copie anonimă a raportului și acolo?",
+  "report.hint": "Sesizarea va fi trimsă către moderatorii acestei instanțe. Poți oferi o explicație pentru această sesizare mai jos:",
+  "report.placeholder": "Comentarii opționale",
+  "report.submit": "Trimite",
+  "report.target": "Raportează {target}",
+  "search.placeholder": "Caută",
+  "search_popout.search_format": "Formate pentru căutare avansată",
+  "search_popout.tips.full_text": "Textele simple returnează statusuri pe care le-ai scris, favorizat, redistribuit, sau în care sunt menționate , deasmenea și utilizatorii sau hastagurile care se potrivesc.",
+  "search_popout.tips.hashtag": "hashtag",
+  "search_popout.tips.status": "status",
+  "search_popout.tips.text": "Textele simple returnează nume, nume de utilizarori și hastagurile care se potrivesc",
+  "search_popout.tips.user": "utilizator",
+  "search_results.accounts": "Oameni",
+  "search_results.hashtags": "Hashtaguri",
+  "search_results.statuses": "Postări",
+  "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
+  "standalone.public_title": "Se întâmplă acum",
+  "status.block": "Blochează @{name}",
+  "status.cancel_reblog_private": "Nedistribuit",
+  "status.cannot_reblog": "Această postare nu poate fi redistribuită",
+  "status.delete": "Șterge",
+  "status.detailed_status": "Conversația detailată",
+  "status.direct": "Mesaj direct @{name}",
+  "status.embed": "Încorporare",
+  "status.favourite": "Favorite",
+  "status.filtered": "Sortate",
+  "status.load_more": "Încarcă mai multe",
+  "status.media_hidden": "Media ascunsă",
+  "status.mention": "Mentionează @{name}",
+  "status.more": "Mai mult",
+  "status.mute": "Oprește @{name}",
+  "status.mute_conversation": "Oprește conversația",
+  "status.open": "Extinde acest status",
+  "status.pin": "Fixează pe profil",
+  "status.pinned": "Postare fixată",
+  "status.reblog": "Redistribuie",
+  "status.reblog_private": "Redistribuie către audiența originală",
+  "status.reblogged_by": "{name} redistribuit",
+  "status.reblogs.empty": "Nimeni nu a redistribuit această postare până acum. Când cineva o va face, va apărea aici.",
+  "status.redraft": "Șterge și adaugă la ciorne",
+  "status.reply": "Răspunde",
+  "status.replyAll": "Răspunde la topic",
+  "status.report": "Raportează @{name}",
+  "status.sensitive_toggle": "Afișează",
+  "status.sensitive_warning": "Conținut sensibil",
+  "status.share": "Distribuie",
+  "status.show_less": "Arată mai puțin",
+  "status.show_less_all": "Arată mai puțin pentru toți",
+  "status.show_more": "Arată mai mult",
+  "status.show_more_all": "Arată mai mult pentru toți",
+  "status.unmute_conversation": "Repornește conversația",
+  "status.unpin": "Eliberează din profil",
+  "tabs_bar.federated_timeline": "Global",
+  "tabs_bar.home": "Acasă",
+  "tabs_bar.local_timeline": "Local",
+  "tabs_bar.notifications": "Notificări",
+  "tabs_bar.search": "Căutare",
+  "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} vorbesc",
+  "ui.beforeunload": "Postarea se va pierde dacă părăsești pagina.",
+  "upload_area.title": "Trage și eliberează pentru a încărca",
+  "upload_button.label": "Adaugă media (JPEG, PNG, GIF, WebM, MP4, MOV)",
+  "upload_form.description": "Adaugă o descriere pentru persoanele cu deficiențe de vedere",
+  "upload_form.focus": "Taie",
+  "upload_form.undo": "Șterge",
+  "upload_progress.label": "Se Încarcă...",
+  "video.close": "Închide video",
+  "video.exit_fullscreen": "Închide",
+  "video.expand": "Extinde video",
+  "video.fullscreen": "Ecran întreg",
+  "video.hide": "Ascunde video",
+  "video.mute": "Oprește sonorul",
+  "video.pause": "Pauză",
+  "video.play": "Redare",
+  "video.unmute": "Repornește sunetul"
+}
diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json
index 8add56641..8961136a3 100644
--- a/app/javascript/mastodon/locales/ru.json
+++ b/app/javascript/mastodon/locales/ru.json
@@ -10,11 +10,12 @@
   "account.endorse": "Рекомендовать в профиле",
   "account.follow": "Подписаться",
   "account.followers": "Подписаны",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "Никто не подписан на этого пользователя.",
   "account.follows": "Подписки",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "Этот пользователь ни на кого не подписан.",
   "account.follows_you": "Подписан(а) на Вас",
   "account.hide_reblogs": "Скрыть продвижения от @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Медиа",
   "account.mention": "Упомянуть",
   "account.moved_to": "Ищите {name} здесь:",
@@ -63,7 +64,7 @@
   "column_header.show_settings": "Показать настройки",
   "column_header.unpin": "Открепить",
   "column_subheading.settings": "Настройки",
-  "community.column_settings.media_only": "Только медиа",
+  "community.column_settings.media_only": "Только с медиа",
   "compose_form.direct_message_warning": "Этот статус будет виден только упомянутым пользователям.",
   "compose_form.direct_message_warning_learn_more": "Узнать больше",
   "compose_form.hashtag_warning": "Этот пост не будет показывается в поиске по хэштегу, т.к. он непубличный. Только публичные посты можно найти в поиске по хэштегу.",
@@ -76,7 +77,7 @@
   "compose_form.sensitive.unmarked": "Медиафайлы не отмечены как чувствительные",
   "compose_form.spoiler.marked": "Текст скрыт за предупреждением",
   "compose_form.spoiler.unmarked": "Текст не скрыт",
-  "compose_form.spoiler_placeholder": "Напишите свое предупреждение здесь",
+  "compose_form.spoiler_placeholder": "Текст предупреждения",
   "confirmation_modal.cancel": "Отмена",
   "confirmations.block.confirm": "Заблокировать",
   "confirmations.block.message": "Вы уверены, что хотите заблокировать {name}?",
@@ -108,19 +109,19 @@
   "emoji_button.search_results": "Результаты поиска",
   "emoji_button.symbols": "Символы",
   "emoji_button.travel": "Путешествия",
-  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.blocks": "Вы ещё никого не заблокировали.",
   "empty_column.community": "Локальная лента пуста. Напишите что-нибудь, чтобы разогреть народ!",
   "empty_column.direct": "У Вас пока нет личных сообщений. Когда Вы начнёте их отправлять или получать, они появятся здесь.",
-  "empty_column.domain_blocks": "There are no hidden 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.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": "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.lists": "У Вас ещё нет списков. Все созданные Вами списки будут показаны здесь.",
+  "empty_column.mutes": "Вы ещё никого не заглушили.",
   "empty_column.notifications": "У Вас еще нет уведомлений. Заведите знакомство с другими пользователями, чтобы начать разговор.",
   "empty_column.public": "Здесь ничего нет! Опубликуйте что-нибудь или подпишитесь на пользователей с других узлов, чтобы заполнить ленту.",
   "follow_request.authorize": "Авторизовать",
@@ -137,32 +138,32 @@
   "home.column_settings.show_reblogs": "Показывать продвижения",
   "home.column_settings.show_replies": "Показывать ответы",
   "keyboard_shortcuts.back": "перейти назад",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "чтобы открыть список заблокированных",
   "keyboard_shortcuts.boost": "продвинуть пост",
   "keyboard_shortcuts.column": "фокус на одном из столбцов",
   "keyboard_shortcuts.compose": "фокус на поле ввода",
   "keyboard_shortcuts.description": "Описание",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "чтобы показать колонку личных сообщений",
   "keyboard_shortcuts.down": "вниз по списку",
   "keyboard_shortcuts.enter": "развернуть пост",
   "keyboard_shortcuts.favourite": "в избранное",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "открыть 'Избранное'",
+  "keyboard_shortcuts.federated": "перейти к глобальной ленте",
   "keyboard_shortcuts.heading": "Сочетания клавиш",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "перейти к домашней ленте",
   "keyboard_shortcuts.hotkey": "Гор. клавиша",
   "keyboard_shortcuts.legend": "показать это окно",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "перейти к локальной ленте",
   "keyboard_shortcuts.mention": "упомянуть автора поста",
-  "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.pinned": "to open pinned toots list",
+  "keyboard_shortcuts.muted": "открыть список заглушённых",
+  "keyboard_shortcuts.my_profile": "перейти к своему профилю",
+  "keyboard_shortcuts.notifications": "перейти к уведомлениям",
+  "keyboard_shortcuts.pinned": "перейти к закреплённым статусам",
   "keyboard_shortcuts.profile": "перейти к профилю автора",
   "keyboard_shortcuts.reply": "ответить",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "перейти к запросам на подписку",
   "keyboard_shortcuts.search": "перейти к поиску",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
+  "keyboard_shortcuts.start": "перейти к разделу \"добро пожаловать\"",
   "keyboard_shortcuts.toggle_hidden": "показать/скрыть текст за предупреждением",
   "keyboard_shortcuts.toot": "начать писать новый пост",
   "keyboard_shortcuts.unfocus": "убрать фокус с поля ввода/поиска",
@@ -186,7 +187,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Список блокировки",
   "navigation_bar.community_timeline": "Локальная лента",
-  "navigation_bar.compose": "Compose new toot",
+  "navigation_bar.compose": "Создать новый статус",
   "navigation_bar.direct": "Личные сообщения",
   "navigation_bar.discover": "Изучайте",
   "navigation_bar.domain_blocks": "Скрытые домены",
@@ -280,7 +281,7 @@
   "status.cancel_reblog_private": "Не продвигать",
   "status.cannot_reblog": "Этот статус не может быть продвинут",
   "status.delete": "Удалить",
-  "status.detailed_status": "Detailed conversation view",
+  "status.detailed_status": "Подробный просмотр обсуждения",
   "status.direct": "Написать @{name}",
   "status.embed": "Встроить",
   "status.favourite": "Нравится",
@@ -290,17 +291,17 @@
   "status.mention": "Упомянуть @{name}",
   "status.more": "Больше",
   "status.mute": "Заглушить @{name}",
-  "status.mute_conversation": "Заглушить тред",
+  "status.mute_conversation": "Заглушить всю цепочку",
   "status.open": "Развернуть статус",
   "status.pin": "Закрепить в профиле",
   "status.pinned": "Закреплённый статус",
   "status.reblog": "Продвинуть",
   "status.reblog_private": "Продвинуть для своей аудитории",
   "status.reblogged_by": "{name} продвинул(а)",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
+  "status.reblogs.empty": "Никто ещё не продвинул этот статус. Как только кто-то это сделает, они появятся здесь.",
   "status.redraft": "Удалить и исправить",
   "status.reply": "Ответить",
-  "status.replyAll": "Ответить на тред",
+  "status.replyAll": "Ответить всем",
   "status.report": "Пожаловаться",
   "status.sensitive_toggle": "Нажмите для просмотра",
   "status.sensitive_warning": "Чувствительный контент",
diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json
index fb5b1eb14..1264b6d08 100644
--- a/app/javascript/mastodon/locales/sk.json
+++ b/app/javascript/mastodon/locales/sk.json
@@ -7,14 +7,15 @@
   "account.disclaimer_full": "Inofrmácie uvedené nižšie nemusia byť úplným odrazom uživateľovho účtu.",
   "account.domain_blocked": "Doména ukrytá",
   "account.edit_profile": "Upraviť profil",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "Zobrazuj na profile",
   "account.follow": "Následuj",
   "account.followers": "Sledujúci",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "Tohto užívateľa ešte nikto nenásleduje.",
   "account.follows": "Následuje",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "Tento užívateľ ešte nikoho nenásleduje.",
   "account.follows_you": "Následuje ťa",
   "account.hide_reblogs": "Skryť povýšenia od @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Médiá",
   "account.mention": "Spomeň @{name}",
   "account.moved_to": "{name} sa presunul/a na:",
@@ -29,7 +30,7 @@
   "account.show_reblogs": "Ukáž povýšenia od @{name}",
   "account.unblock": "Odblokovať @{name}",
   "account.unblock_domain": "Prestať blokovať {domain}",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "Nezobrazuj na profile",
   "account.unfollow": "Prestať nasledovať",
   "account.unmute": "Prestať ignorovať @{name}",
   "account.unmute_notifications": "Odtĺmiť notifikácie od @{name}",
@@ -89,7 +90,7 @@
   "confirmations.mute.confirm": "Ignoruj",
   "confirmations.mute.message": "Naozaj chcete ignorovať {name}?",
   "confirmations.redraft.confirm": "Vyčistiť a prepísať",
-  "confirmations.redraft.message": "Si si istý/á, že chceš vymazať a prepísať tento príspevok? Stratíš všetky jeho nadobudnuté odpovede, povýšenia a obľúbenia.",
+  "confirmations.redraft.message": "Si si istý/á, že chceš premazať a prepísať tento príspevok? Jeho nadobudnuté odpovede, povýšenia a obľúbenia, ale i odpovede na pôvodný príspevok budú odlúčené.",
   "confirmations.unfollow.confirm": "Nesledovať",
   "confirmations.unfollow.message": "Naozaj chcete prestať sledovať {name}?",
   "embed.instructions": "Umiestni kód uvedený nižšie pre pridanie tohto statusu na tvoju web stránku.",
@@ -108,19 +109,19 @@
   "emoji_button.search_results": "Nájdené",
   "emoji_button.symbols": "Symboly",
   "emoji_button.travel": "Cestovanie a miesta",
-  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.blocks": "Ešte si nikoho nezablokoval/a.",
   "empty_column.community": "Lokálna časová os je prázdna. Napíšte niečo, aby sa to tu začalo hýbať!",
   "empty_column.direct": "Ešte nemáš žiadne súkromné správy. Keď nejakú pošleš, alebo dostaneš, ukáže sa tu.",
-  "empty_column.domain_blocks": "There are no hidden 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.domain_blocks": "Žiadne domény ešte niesú skryté.",
+  "empty_column.favourited_statuses": "Nemáš obľúbené ešte žiadne príspevky. Keď si nejaký obľúbiš, bude zobrazený práve tu.",
+  "empty_column.favourites": "Tento toot si ešte nikto neobľúbil. Ten kto si ho obľúbi, bude zobrazený tu.",
+  "empty_column.follow_requests": "Ešte nemáš žiadne požiadavky o následovanie. Keď nejaké dostaneš, budú tu zobrazené.",
   "empty_column.hashtag": "Pod týmto hashtagom sa ešte nič nenachádza.",
   "empty_column.home": "Tvoja lokálna osa je zatiaľ prázdna! Pre začiatok navštív {public}, alebo použi vyhľadávanie a nájdi tak aj iných užívateľov.",
   "empty_column.home.public_timeline": "verejná časová os",
   "empty_column.list": "Tento zoznam je ešte prázdny. Keď ale členovia tohoto zoznamu napíšu nové správy, tak tie sa objavia priamo tu.",
-  "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.lists": "Nemáš ešte žiadne zoznamy. Keď nejaký vytvoríš, bude zobrazený práve tu.",
+  "empty_column.mutes": "Ešte si nestĺmil žiadných užívateľov.",
   "empty_column.notifications": "Nemáš ešte žiadne oznámenia. Zapoj sa s niekym do debaty a komunikuj s ostatnými aby diskusia mohla začať.",
   "empty_column.public": "Ešte tu nič nie je. Napíš niečo verejne alebo začnite sledovať užívateľov z iných Mastodon serverov, aby tu tak niečo pribudlo",
   "follow_request.authorize": "Povoľ prístup",
@@ -137,32 +138,32 @@
   "home.column_settings.show_reblogs": "Zobraziť povýšené",
   "home.column_settings.show_replies": "Ukázať odpovede",
   "keyboard_shortcuts.back": "dostať sa naspäť",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "otvor zoznam blokovaných užívateľov",
   "keyboard_shortcuts.boost": "vyzdvihnúť",
   "keyboard_shortcuts.column": "zamerať sa na status v jednom zo stĺpcov",
   "keyboard_shortcuts.compose": "zamerať sa na písaciu plochu",
   "keyboard_shortcuts.description": "Popis",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "otvor panel súkromných správ",
   "keyboard_shortcuts.down": "posunúť sa dole v zozname",
   "keyboard_shortcuts.enter": "otvoriť správu",
   "keyboard_shortcuts.favourite": "pridať do obľúbených",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "otvor zoznam obľúbených",
+  "keyboard_shortcuts.federated": "otvor federovanú časovú os",
   "keyboard_shortcuts.heading": "Klávesové skratky",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "otvor domácu časovú os",
   "keyboard_shortcuts.hotkey": "Klávesa",
   "keyboard_shortcuts.legend": "zobraziť túto legendu",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "otvor miestnu časovú os",
   "keyboard_shortcuts.mention": "spomenúť autora",
-  "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.pinned": "to open pinned toots list",
-  "keyboard_shortcuts.profile": "to open author's profile",
+  "keyboard_shortcuts.muted": "otvor zoznam stíšených užívateľov",
+  "keyboard_shortcuts.my_profile": "otvor svoj profil",
+  "keyboard_shortcuts.notifications": "otvor panel oboznámení",
+  "keyboard_shortcuts.pinned": "otvor zoznam pripnutých príspevkov",
+  "keyboard_shortcuts.profile": "otvor autorov profil",
   "keyboard_shortcuts.reply": "odpovedať",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "otvor zoznam požiadavok k následovaniu",
   "keyboard_shortcuts.search": "zamerať sa na vyhľadávanie",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
+  "keyboard_shortcuts.start": "otvor panel ''začíname''",
   "keyboard_shortcuts.toggle_hidden": "ukáž/skry text za CW",
   "keyboard_shortcuts.toot": "začať úplne novú hlášku",
   "keyboard_shortcuts.unfocus": "nesústrediť sa na písaciu plochu, alebo hľadanie",
@@ -183,10 +184,10 @@
   "missing_indicator.label": "Nenájdené",
   "missing_indicator.sublabel": "Tento zdroj sa nepodarilo nájsť",
   "mute_modal.hide_notifications": "Skryť notifikácie od tohoto užívateľa?",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "Mobilné aplikácie",
   "navigation_bar.blocks": "Blokovaní užívatelia",
   "navigation_bar.community_timeline": "Lokálna časová os",
-  "navigation_bar.compose": "Compose new toot",
+  "navigation_bar.compose": "Napíš nový príspevok",
   "navigation_bar.direct": "Súkromné správy",
   "navigation_bar.discover": "Objavuj",
   "navigation_bar.domain_blocks": "Skryté domény",
@@ -280,7 +281,7 @@
   "status.cancel_reblog_private": "Nezdieľaj",
   "status.cannot_reblog": "Tento príspevok nemôže byť re-tootnutý",
   "status.delete": "Zmazať",
-  "status.detailed_status": "Detailed conversation view",
+  "status.detailed_status": "Podrobný náhľad celej konverzácie",
   "status.direct": "Súkromná správa @{name}",
   "status.embed": "Vložiť",
   "status.favourite": "Páči sa mi",
@@ -297,7 +298,7 @@
   "status.reblog": "Povýšiť",
   "status.reblog_private": "Povýš k pôvodnému publiku",
   "status.reblogged_by": "{name} povýšil/a",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
+  "status.reblogs.empty": "Nikto ešte nepovýšil tento príspevok. Keď tak niekto urobí, bude to zobrazené práve tu.",
   "status.redraft": "Vymaž a prepíš",
   "status.reply": "Odpovedať",
   "status.replyAll": "Odpovedať na diskusiu",
@@ -319,7 +320,7 @@
   "trends.count_by_accounts": "{count} {rawCount, viacerí, jeden {person} iní {people}} diskutujú",
   "ui.beforeunload": "Čo máš rozpísané sa stratí, ak opustíš Mastodon.",
   "upload_area.title": "Pretiahni a pusť pre nahratie",
-  "upload_button.label": "Pridať médiá",
+  "upload_button.label": "Pridať médiálny súbor (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_form.description": "Opis pre slabo vidiacich",
   "upload_form.focus": "Vystrihni",
   "upload_form.undo": "Vymaž",
diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json
index 8f6c2e3d5..6250aab62 100644
--- a/app/javascript/mastodon/locales/sl.json
+++ b/app/javascript/mastodon/locales/sl.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "Ti sledi",
   "account.hide_reblogs": "Skrij napuhke od @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Mediji",
   "account.mention": "Omeni @{name}",
   "account.moved_to": "{name} se je premaknil na:",
diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json
index 0f56f642a..0ef07a170 100644
--- a/app/javascript/mastodon/locales/sr-Latn.json
+++ b/app/javascript/mastodon/locales/sr-Latn.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "Prati Vas",
   "account.hide_reblogs": "Sakrij podrške koje daje korisnika @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Mediji",
   "account.mention": "Pomeni korisnika @{name}",
   "account.moved_to": "{name} se pomerio na:",
diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json
index e727a06a7..ca2c1b225 100644
--- a/app/javascript/mastodon/locales/sr.json
+++ b/app/javascript/mastodon/locales/sr.json
@@ -1,41 +1,42 @@
 {
-  "account.badges.bot": "Bot",
-  "account.block": "Блокирај корисника @{name}",
+  "account.badges.bot": "Бот",
+  "account.block": "Блокирај @{name}",
   "account.block_domain": "Сакриј све са домена {domain}",
-  "account.blocked": "Blocked",
-  "account.direct": "Direct Message @{name}",
+  "account.blocked": "Блокиран",
+  "account.direct": "Директна порука @{name}",
   "account.disclaimer_full": "Наведене информације можда не одсликавају кориснички профил у потпуности.",
-  "account.domain_blocked": "Domain hidden",
+  "account.domain_blocked": "Домен сакривен",
   "account.edit_profile": "Измени профил",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "Приказати на профилу",
   "account.follow": "Запрати",
-  "account.followers": "Пратиоца",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers": "Пратиоци",
+  "account.followers.empty": "Тренутно нико не прати овог корисника.",
   "account.follows": "Прати",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "Корисник тренутно не прати никога.",
   "account.follows_you": "Прати Вас",
   "account.hide_reblogs": "Сакриј подршке које даје корисника @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Медији",
   "account.mention": "Помени корисника @{name}",
   "account.moved_to": "{name} се померио на:",
   "account.mute": "Ућуткај корисника @{name}",
   "account.mute_notifications": "Искључи обавештења од корисника @{name}",
-  "account.muted": "Muted",
-  "account.posts": "Статуса",
-  "account.posts_with_replies": "Toots with replies",
+  "account.muted": "Ућуткан",
+  "account.posts": "Трубе",
+  "account.posts_with_replies": "Трубе и одговори",
   "account.report": "Пријави @{name}",
   "account.requested": "Чекам одобрење. Кликните да поништите захтев за праћење",
   "account.share": "Подели профил корисника @{name}",
   "account.show_reblogs": "Прикажи подршке од корисника @{name}",
   "account.unblock": "Одблокирај корисника @{name}",
   "account.unblock_domain": "Одблокирај домен {domain}",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "Не истичи на профилу",
   "account.unfollow": "Отпрати",
   "account.unmute": "Уклони ућуткавање кориснику @{name}",
   "account.unmute_notifications": "Укључи назад обавештења од корисника @{name}",
   "account.view_full_profile": "Види цео профил",
-  "alert.unexpected.message": "An unexpected error occurred.",
-  "alert.unexpected.title": "Oops!",
+  "alert.unexpected.message": "Појавила се неочекивана грешка.",
+  "alert.unexpected.title": "Упс!",
   "boost_modal.combo": "Можете притиснути {combo} да прескочите ово следећи пут",
   "bundle_column_error.body": "Нешто је пошло по злу приликом учитавања ове компоненте.",
   "bundle_column_error.retry": "Покушајте поново",
@@ -44,17 +45,17 @@
   "bundle_modal_error.message": "Нешто није било у реду при учитавању ове компоненте.",
   "bundle_modal_error.retry": "Покушајте поново",
   "column.blocks": "Блокирани корисници",
-  "column.community": "Локална лајна",
-  "column.direct": "Direct messages",
-  "column.domain_blocks": "Hidden domains",
-  "column.favourites": "Омиљени",
+  "column.community": "Локална временска линија",
+  "column.direct": "Директне поруке",
+  "column.domain_blocks": "Скривени домени",
+  "column.favourites": "Омиљене",
   "column.follow_requests": "Захтеви за праћење",
   "column.home": "Почетна",
   "column.lists": "Листе",
   "column.mutes": "Ућуткани корисници",
   "column.notifications": "Обавештења",
-  "column.pins": "Прикачени тутови",
-  "column.public": "Федерисана лајна",
+  "column.pins": "Прикачене трубе",
+  "column.public": "Здружена временска линија",
   "column_back_button.label": "Назад",
   "column_header.hide_settings": "Сакриј поставке",
   "column_header.moveLeft_settings": "Помери колону улево",
@@ -63,19 +64,19 @@
   "column_header.show_settings": "Прикажи поставке",
   "column_header.unpin": "Откачи",
   "column_subheading.settings": "Поставке",
-  "community.column_settings.media_only": "Media Only",
-  "compose_form.direct_message_warning": "This toot will only be visible 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.",
+  "community.column_settings.media_only": "Само Медији",
+  "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.placeholder": "Шта Вам је на уму?",
-  "compose_form.publish": "Тутни",
+  "compose_form.publish": "Труби",
   "compose_form.publish_loud": "{publish}!",
-  "compose_form.sensitive.marked": "Media is marked as sensitive",
-  "compose_form.sensitive.unmarked": "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.sensitive.marked": "Медији су означени као осетљиви",
+  "compose_form.sensitive.unmarked": "Медији су означени као не-осетљиви",
+  "compose_form.spoiler.marked": "Текст је сакривен иза упозорења",
+  "compose_form.spoiler.unmarked": "Текст није сакривен",
   "compose_form.spoiler_placeholder": "Овде упишите упозорење",
   "confirmation_modal.cancel": "Поништи",
   "confirmations.block.confirm": "Блокирај",
@@ -85,11 +86,11 @@
   "confirmations.delete_list.confirm": "Обриши",
   "confirmations.delete_list.message": "Да ли сте сигурни да желите да бесповратно обришете ову листу?",
   "confirmations.domain_block.confirm": "Сакриј цео домен",
-  "confirmations.domain_block.message": "Да ли сте стварно, стварно сигурно да желите да блокирате цео домен {domain}? У већини случајева, пар добрих блокирања или ућуткавања су довољна и препоручљива.",
+  "confirmations.domain_block.message": "Да ли сте заиста сигурни да желите да блокирате цео домен {domain}? У већини случајева, неколико добро промишљених блокирања или ућуткавања су довољна и препоручљива.",
   "confirmations.mute.confirm": "Ућуткај",
   "confirmations.mute.message": "Да ли стварно желите да ућуткате корисника {name}?",
-  "confirmations.redraft.confirm": "Delete & redraft",
-  "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.",
+  "confirmations.redraft.confirm": "Избриши и преправи",
+  "confirmations.redraft.message": "Да ли сте сигурни да желите да избришете овај статус и да га преправите? Сва стављања у омиљене трубе, као и подршке ће бити изгубљене, а одговори на оригинални пост ће бити поништени.",
   "confirmations.unfollow.confirm": "Отпрати",
   "confirmations.unfollow.message": "Да ли сте сигурни да желите да отпратите корисника {name}?",
   "embed.instructions": "Угради овај статус на Ваш веб сајт копирањем кода испод.",
@@ -97,75 +98,75 @@
   "emoji_button.activity": "Активност",
   "emoji_button.custom": "Произвољно",
   "emoji_button.flags": "Заставе",
-  "emoji_button.food": "Храна & пиће",
-  "emoji_button.label": "Убаци смајли",
+  "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": "Најчешће коришћени",
   "emoji_button.search": "Претрага...",
   "emoji_button.search_results": "Резултати претраге",
   "emoji_button.symbols": "Симболи",
-  "emoji_button.travel": "Путовања & места",
-  "empty_column.blocks": "You haven't blocked any users yet.",
-  "empty_column.community": "Локална лајна је празна. Напишите нешто јавно да лајна продува!",
-  "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 hidden 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": "Тренутно нема ништа на овом хештегу.",
-  "empty_column.home": "Ваша лајна је празна! Посетите {public} или користите претрагу да почнете и упознајете нове људе.",
-  "empty_column.home.public_timeline": "јавна лајна",
-  "empty_column.list": "У овој листи још нема ничега. Када чланови листе објаве нове статусе, они ће се појављивати овде.",
-  "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": "Тренутно немате обавештења. Дружите се мало да започнете разговоре.",
+  "emoji_button.travel": "Путовања и места",
+  "empty_column.blocks": "Још увек немате блокираних корисника.",
+  "empty_column.community": "Локална временска линија је празна. Напишите нешто јавно да започнете!",
+  "empty_column.direct": "Још увек немате директних порука. Када пошаљете или примите једну, појавиће се овде.",
+  "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.notifications": "Тренутно немате обавештења. Дружите се мало да започнете разговор.",
   "empty_column.public": "Овде нема ничега! Напишите нешто јавно, или нађите кориснике са других инстанци које ћете запратити да попуните ову празнину",
   "follow_request.authorize": "Одобри",
   "follow_request.reject": "Одбиј",
-  "getting_started.developers": "Developers",
-  "getting_started.documentation": "Documentation",
-  "getting_started.find_friends": "Find friends from Twitter",
+  "getting_started.developers": "Програмери",
+  "getting_started.documentation": "Документација",
+  "getting_started.find_friends": "Пронађите пријатеље са Твитера",
   "getting_started.heading": "Да почнете",
-  "getting_started.invite": "Invite people",
-  "getting_started.open_source_notice": "Мастoдонт је софтвер отвореног кода. Можете му допринети или пријавити проблеме преко GitHub-а на {github}.",
-  "getting_started.security": "Security",
-  "getting_started.terms": "Terms of service",
+  "getting_started.invite": "Позовите људе",
+  "getting_started.open_source_notice": "Мастoдон је софтвер отвореног кода. Можете му допринети или пријавити проблеме преко ГитХаба на {github}.",
+  "getting_started.security": "Безбедност",
+  "getting_started.terms": "Услови коришћења",
   "home.column_settings.basic": "Основно",
   "home.column_settings.show_reblogs": "Прикажи и подржавања",
   "home.column_settings.show_replies": "Прикажи одговоре",
   "keyboard_shortcuts.back": "да одете назад",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "да отворите листу блокираних корисника",
   "keyboard_shortcuts.boost": "да подржите",
   "keyboard_shortcuts.column": "да се пребаците на статус у једној од колона",
   "keyboard_shortcuts.compose": "да се пребаците на писање новог тута",
   "keyboard_shortcuts.description": "Опис",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "да отворите колону за директне поруке",
   "keyboard_shortcuts.down": "да се померите на доле у листи",
   "keyboard_shortcuts.enter": "да отворите статус",
   "keyboard_shortcuts.favourite": "да означите као омиљено",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "да отворите листу фаворита",
+  "keyboard_shortcuts.federated": "да отворите здружену временску линију",
   "keyboard_shortcuts.heading": "Пречице на тастатури",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "да отворите временску линију почетне",
   "keyboard_shortcuts.hotkey": "Пречица",
   "keyboard_shortcuts.legend": "да прикажете овај подсетник",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "да отворите локалну временску линију",
   "keyboard_shortcuts.mention": "да поменете аутора",
-  "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.pinned": "to open pinned toots list",
-  "keyboard_shortcuts.profile": "to open author's profile",
+  "keyboard_shortcuts.muted": "да отворите листу ућутканих корисника",
+  "keyboard_shortcuts.my_profile": "да отворите ваш профил",
+  "keyboard_shortcuts.notifications": "да отворите колону обавештења",
+  "keyboard_shortcuts.pinned": "да отворите листу закачених труба",
+  "keyboard_shortcuts.profile": "да отворите профил аутора",
   "keyboard_shortcuts.reply": "да одговорите",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "да отворите листу примљених захтева за праћење",
   "keyboard_shortcuts.search": "да се пребаците на претрагу",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
-  "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW",
-  "keyboard_shortcuts.toot": "да започнете скроз нови тут",
-  "keyboard_shortcuts.unfocus": "да не будете више на претрази/прављењу новог тута",
+  "keyboard_shortcuts.start": "да отворите колону \"почнимо\"",
+  "keyboard_shortcuts.toggle_hidden": "да прикажете/сакријте текст иза CW-а",
+  "keyboard_shortcuts.toot": "да започнете скроз нову трубу",
+  "keyboard_shortcuts.unfocus": "да одфокусирате/не будете више на претрази/прављењу нове трубе",
   "keyboard_shortcuts.up": "да се померите на горе у листи",
   "lightbox.close": "Затвори",
   "lightbox.next": "Следећи",
@@ -181,18 +182,18 @@
   "loading_indicator.label": "Учитавам...",
   "media_gallery.toggle_visible": "Укључи/искључи видљивост",
   "missing_indicator.label": "Није пронађено",
-  "missing_indicator.sublabel": "This resource could not be found",
+  "missing_indicator.sublabel": "Овај ресурс није пронађен",
   "mute_modal.hide_notifications": "Сакриј обавештења од овог корисника?",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "Мобилне апликације",
   "navigation_bar.blocks": "Блокирани корисници",
-  "navigation_bar.community_timeline": "Локална лајна",
-  "navigation_bar.compose": "Compose new toot",
-  "navigation_bar.direct": "Direct messages",
-  "navigation_bar.discover": "Discover",
-  "navigation_bar.domain_blocks": "Hidden domains",
+  "navigation_bar.community_timeline": "Локална временска линија",
+  "navigation_bar.compose": "Саставите нову трубу",
+  "navigation_bar.direct": "Директне поруке",
+  "navigation_bar.discover": "Откриј",
+  "navigation_bar.domain_blocks": "Сакривени домени",
   "navigation_bar.edit_profile": "Измени профил",
-  "navigation_bar.favourites": "Омиљени",
-  "navigation_bar.filters": "Muted words",
+  "navigation_bar.favourites": "Омиљене",
+  "navigation_bar.filters": "Пригушене речи",
   "navigation_bar.follow_requests": "Захтеви за праћење",
   "navigation_bar.info": "О овој инстанци",
   "navigation_bar.keyboard_shortcuts": "Пречице на тастатури",
@@ -200,14 +201,14 @@
   "navigation_bar.logout": "Одјава",
   "navigation_bar.mutes": "Ућуткани корисници",
   "navigation_bar.personal": "Personal",
-  "navigation_bar.pins": "Прикачени тутови",
+  "navigation_bar.pins": "Прикачене трубе",
   "navigation_bar.preferences": "Подешавања",
-  "navigation_bar.public_timeline": "Федерисана лајна",
-  "navigation_bar.security": "Security",
-  "notification.favourite": "{name} је ставио Ваш статус као омиљени",
-  "notification.follow": "{name} Вас је запратио",
-  "notification.mention": "{name} Вас је поменуо",
-  "notification.reblog": "{name} је подржао(ла) Ваш статус",
+  "navigation_bar.public_timeline": "Здружена временска линија",
+  "navigation_bar.security": "Безбедност",
+  "notification.favourite": "{name} је ставио/ла Ваш статус као омиљени",
+  "notification.follow": "{name} Вас је запратио/ла",
+  "notification.mention": "{name} Вас је поменуо/ла",
+  "notification.reblog": "{name} је подржао/ла Ваш статус",
   "notifications.clear": "Очисти обавештења",
   "notifications.clear_confirmation": "Да ли сте сигурно да трајно желите да очистите Ваша обавештења?",
   "notifications.column_settings.alert": "Обавештења на радној површини",
@@ -219,21 +220,21 @@
   "notifications.column_settings.reblog": "Подршки:",
   "notifications.column_settings.show": "Прикажи у колони",
   "notifications.column_settings.sound": "Пуштај звук",
-  "notifications.group": "{count} notifications",
+  "notifications.group": "{count} обавештења",
   "onboarding.done": "Готово",
   "onboarding.next": "Следеће",
-  "onboarding.page_five.public_timelines": "Локална лајна приказује све јавне статусе од свих на домену {domain}. Федерисана лајна приказује јавне статусе од свих људи које прате корисници са домена {domain}. Ово су јавне лајне, сјајан начин да откријете нове људе.",
-  "onboarding.page_four.home": "Почетна лајна приказује статусе људи које Ви пратите.",
+  "onboarding.page_five.public_timelines": "Локална временска линија приказује све јавне статусе од свих на домену {domain}. Здружена временска линија приказује јавне статусе од свих људи које прате корисници са домена {domain}. Ово су јавне временске линије, сјајан начин да откријете нове људе.",
+  "onboarding.page_four.home": "Почетна временска линија приказује статусе људи које Ви пратите.",
   "onboarding.page_four.notifications": "Колона са обавештењима Вам приказује када неко прича са Вама.",
   "onboarding.page_one.federation": "Мастодонт је мрежа независних сервера који се увезују да направе једну већу друштвену мрежу. Ове сервере зовемо инстанцама.",
-  "onboarding.page_one.full_handle": "Your full handle",
-  "onboarding.page_one.handle_hint": "This is what you would tell your friends to search for.",
-  "onboarding.page_one.welcome": "Добродошли на Мастодонт!",
+  "onboarding.page_one.full_handle": "Ваш пун надимак",
+  "onboarding.page_one.handle_hint": "Ово бисте рекли својим пријатељима да траже.",
+  "onboarding.page_one.welcome": "Добродошли на Мастодон!",
   "onboarding.page_six.admin": "Администратор Ваше инстанце је {admin}.",
   "onboarding.page_six.almost_done": "Још мало, па готово...",
   "onboarding.page_six.appetoot": "Пријатутно!",
   "onboarding.page_six.apps_available": "Постоје {apps} доступне за iOS, Андроид и друге платформе.",
-  "onboarding.page_six.github": "Мастодонт је слободан софтвер отвореног кода. Можете пријављивати грешке, потраживати нове фунцкионалности, или учествујући у програмирању. Наш изворни код је овде: {github}.",
+  "onboarding.page_six.github": "Мастодон је слободан софтвер отвореног кода. Можете пријављивати грешке, потраживати нове фунцкионалности, или учествујући у програмирању. Наш изворни код је овде {github}.",
   "onboarding.page_six.guidelines": "смернице заједнице",
   "onboarding.page_six.read_guidelines": "Прочитејте {guidelines} домена {domain}!",
   "onboarding.page_six.various_app": "мобилне апликације",
@@ -248,57 +249,57 @@
   "privacy.private.short": "Само за пратиоце",
   "privacy.public.long": "Објави на јавној лајни",
   "privacy.public.short": "Јавно",
-  "privacy.unlisted.long": "Не објављуј на јавним лајнама",
+  "privacy.unlisted.long": "Не објављуј на јавним временским линијама",
   "privacy.unlisted.short": "Неизлистано",
-  "regeneration_indicator.label": "Loading…",
-  "regeneration_indicator.sublabel": "Your home feed is being prepared!",
+  "regeneration_indicator.label": "Учитавање…",
+  "regeneration_indicator.sublabel": "Ваша почетна страница се припрема!",
   "relative_time.days": "{number}d",
   "relative_time.hours": "{number}h",
   "relative_time.just_now": "сада",
   "relative_time.minutes": "{number}m",
   "relative_time.seconds": "{number}s",
   "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 instance moderators. You can provide an explanation of why you are reporting this account below:",
+  "report.forward": "Проследити {target}",
+  "report.forward_hint": "Налог је са другог сервера. Послати анонимну копију пријаве и тамо?",
+  "report.hint": "Пријава ће бити послата модераторима ваше инстанце. Можете додати објашњење зашто пријављујете овај налог у наставку:",
   "report.placeholder": "Додатни коментари",
   "report.submit": "Пошаљи",
   "report.target": "Пријављујем {target}",
   "search.placeholder": "Претрага",
   "search_popout.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.full_text": "Једноставан текст враћа статусе које сте написали, фаворизовали, подржали или били поменути, као и подударање корисничких имена, приказаних имена, и тараба.",
   "search_popout.tips.hashtag": "хештег",
   "search_popout.tips.status": "статус",
   "search_popout.tips.text": "Тражењем обичног текста ћете добити сва пронађена имена, сва корисничка имена и све нађене хештегове",
   "search_popout.tips.user": "корисник",
-  "search_results.accounts": "People",
-  "search_results.hashtags": "Hashtags",
-  "search_results.statuses": "Toots",
+  "search_results.accounts": "Људи",
+  "search_results.hashtags": "Тарабе",
+  "search_results.statuses": "Трубе",
   "search_results.total": "{count, number} {count, plural, one {резултат} few {резултата} other {резултата}}",
   "standalone.public_title": "Поглед изнутра...",
-  "status.block": "Block @{name}",
-  "status.cancel_reblog_private": "Unboost",
+  "status.block": "Блокирај @{name}",
+  "status.cancel_reblog_private": "Уклони подршку",
   "status.cannot_reblog": "Овај статус не може да се подржи",
   "status.delete": "Обриши",
-  "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.detailed_status": "Детаљни преглед разговора",
+  "status.direct": "Директна порука @{name}",
   "status.embed": "Угради на сајт",
   "status.favourite": "Омиљено",
-  "status.filtered": "Filtered",
+  "status.filtered": "Филтрирано",
   "status.load_more": "Учитај још",
   "status.media_hidden": "Мултимедија сакривена",
   "status.mention": "Помени корисника @{name}",
   "status.more": "Још",
-  "status.mute": "Mute @{name}",
+  "status.mute": "Ућуткај @{name}",
   "status.mute_conversation": "Ућуткај преписку",
   "status.open": "Прошири овај статус",
-  "status.pin": "Прикачи на профил",
-  "status.pinned": "Pinned toot",
+  "status.pin": "Закачи на профил",
+  "status.pinned": "Закачена труба",
   "status.reblog": "Подржи",
-  "status.reblog_private": "Boost to original audience",
-  "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.reblog_private": "Подржи да види првобитна публика",
+  "status.reblogged_by": "{name} подржао/ла",
+  "status.reblogs.empty": "Још увек нико није подржао ову трубу. Када буде подржана, појавиће се овде.",
+  "status.redraft": "Избриши и преправи",
   "status.reply": "Одговори",
   "status.replyAll": "Одговори на дискусију",
   "status.report": "Пријави корисника @{name}",
@@ -306,23 +307,23 @@
   "status.sensitive_warning": "Осетљив садржај",
   "status.share": "Подели",
   "status.show_less": "Прикажи мање",
-  "status.show_less_all": "Show less for all",
+  "status.show_less_all": "Прикажи мање за све",
   "status.show_more": "Прикажи више",
-  "status.show_more_all": "Show more for all",
+  "status.show_more_all": "Прикажи више за све",
   "status.unmute_conversation": "Укључи преписку",
   "status.unpin": "Откачи са профила",
   "tabs_bar.federated_timeline": "Федерисано",
   "tabs_bar.home": "Почетна",
   "tabs_bar.local_timeline": "Локално",
   "tabs_bar.notifications": "Обавештења",
-  "tabs_bar.search": "Search",
-  "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking",
+  "tabs_bar.search": "Претрага",
+  "trends.count_by_accounts": "{count} {rawCount, plural, one {човек} other {људи}} прича",
   "ui.beforeunload": "Ако напустите Мастодонт, изгубићете написани нацрт.",
   "upload_area.title": "Превуците овде да отпремите",
-  "upload_button.label": "Додај мултимедију",
-  "upload_form.description": "Опиши за слабовиде особе",
-  "upload_form.focus": "Crop",
-  "upload_form.undo": "Опозови",
+  "upload_button.label": "Додај мултимедију (JPEG, PNG, GIF, WebM, MP4, MOV)",
+  "upload_form.description": "Опишите за особе са оштећеним видом",
+  "upload_form.focus": "Подесите",
+  "upload_form.undo": "Обриши",
   "upload_progress.label": "Отпремам...",
   "video.close": "Затвори видео",
   "video.exit_fullscreen": "Напусти цео екран",
diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json
index 498f6b411..c7df96cf5 100644
--- a/app/javascript/mastodon/locales/sv.json
+++ b/app/javascript/mastodon/locales/sv.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "Följer dig",
   "account.hide_reblogs": "Dölj knuffar från @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Media",
   "account.mention": "Nämna @{name}",
   "account.moved_to": "{name} har flyttat till:",
diff --git a/app/javascript/mastodon/locales/ta.json b/app/javascript/mastodon/locales/ta.json
new file mode 100644
index 000000000..e0237c023
--- /dev/null
+++ b/app/javascript/mastodon/locales/ta.json
@@ -0,0 +1,337 @@
+{
+  "account.badges.bot": "Bot",
+  "account.block": "Block @{name}",
+  "account.block_domain": "Hide everything from {domain}",
+  "account.blocked": "Blocked",
+  "account.direct": "Direct message @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.domain_blocked": "Domain hidden",
+  "account.edit_profile": "Edit profile",
+  "account.endorse": "Feature on profile",
+  "account.follow": "Follow",
+  "account.followers": "Followers",
+  "account.followers.empty": "No one follows this user yet.",
+  "account.follows": "Follows",
+  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows_you": "Follows you",
+  "account.hide_reblogs": "Hide boosts from @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "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.posts": "Toots",
+  "account.posts_with_replies": "Toots and replies",
+  "account.report": "Report @{name}",
+  "account.requested": "Awaiting approval. Click to cancel follow request",
+  "account.share": "Share @{name}'s profile",
+  "account.show_reblogs": "Show boosts from @{name}",
+  "account.unblock": "Unblock @{name}",
+  "account.unblock_domain": "Unhide {domain}",
+  "account.unendorse": "Don't feature on profile",
+  "account.unfollow": "Unfollow",
+  "account.unmute": "Unmute @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
+  "account.view_full_profile": "View full profile",
+  "alert.unexpected.message": "An unexpected error occurred.",
+  "alert.unexpected.title": "Oops!",
+  "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.community": "Local timeline",
+  "column.direct": "Direct messages",
+  "column.domain_blocks": "Hidden 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.media_only": "Media 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.publish": "Toot",
+  "compose_form.publish_loud": "{publish}!",
+  "compose_form.sensitive.marked": "Media is marked as sensitive",
+  "compose_form.sensitive.unmarked": "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.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.mute.confirm": "Mute",
+  "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.unfollow.confirm": "Unfollow",
+  "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
+  "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.blocks": "You haven't blocked any users yet.",
+  "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 hidden 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 instances to fill it up",
+  "follow_request.authorize": "Authorize",
+  "follow_request.reject": "Reject",
+  "getting_started.developers": "Developers",
+  "getting_started.documentation": "Documentation",
+  "getting_started.find_friends": "Find friends from Twitter",
+  "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",
+  "home.column_settings.basic": "Basic",
+  "home.column_settings.show_reblogs": "Show boosts",
+  "home.column_settings.show_replies": "Show replies",
+  "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.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.start": "to open \"get started\" column",
+  "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW",
+  "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.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.new.create": "Add list",
+  "lists.new.title_placeholder": "New list title",
+  "lists.search": "Search among people you follow",
+  "lists.subheading": "Your lists",
+  "loading_indicator.label": "Loading...",
+  "media_gallery.toggle_visible": "Toggle visibility",
+  "missing_indicator.label": "Not found",
+  "missing_indicator.sublabel": "This resource could not be found",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
+  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.blocks": "Blocked users",
+  "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.info": "About this instance",
+  "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.mention": "{name} mentioned you",
+  "notification.reblog": "{name} boosted your status",
+  "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.follow": "New followers:",
+  "notifications.column_settings.mention": "Mentions:",
+  "notifications.column_settings.push": "Push notifications",
+  "notifications.column_settings.push_meta": "This device",
+  "notifications.column_settings.reblog": "Boosts:",
+  "notifications.column_settings.show": "Show in column",
+  "notifications.column_settings.sound": "Play sound",
+  "notifications.group": "{count} notifications",
+  "onboarding.done": "Done",
+  "onboarding.next": "Next",
+  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.full_handle": "Your full handle",
+  "onboarding.page_one.handle_hint": "This is what you would tell your friends to search for.",
+  "onboarding.page_one.welcome": "Welcome to Mastodon!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+  "onboarding.page_six.almost_done": "Almost done...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "community guidelines",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+  "onboarding.page_six.various_app": "mobile apps",
+  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.skip": "Skip",
+  "privacy.change": "Adjust status privacy",
+  "privacy.direct.long": "Post to mentioned users only",
+  "privacy.direct.short": "Direct",
+  "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",
+  "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",
+  "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 instance 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.total": "{count, number} {count, plural, one {result} other {results}}",
+  "standalone.public_title": "A look inside...",
+  "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
+  "status.cannot_reblog": "This post cannot be boosted",
+  "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.reblog": "Boost",
+  "status.reblog_private": "Boost to original audience",
+  "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.reply": "Reply",
+  "status.replyAll": "Reply to thread",
+  "status.report": "Report @{name}",
+  "status.sensitive_toggle": "Click to view",
+  "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.unmute_conversation": "Unmute conversation",
+  "status.unpin": "Unpin from profile",
+  "tabs_bar.federated_timeline": "Federated",
+  "tabs_bar.home": "Home",
+  "tabs_bar.local_timeline": "Local",
+  "tabs_bar.notifications": "Notifications",
+  "tabs_bar.search": "Search",
+  "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
+  "upload_area.title": "Drag & drop to upload",
+  "upload_button.label": "Add media (JPEG, PNG, GIF, WebM, MP4, MOV)",
+  "upload_form.description": "Describe for the visually impaired",
+  "upload_form.focus": "Crop",
+  "upload_form.undo": "Delete",
+  "upload_progress.label": "Uploading...",
+  "video.close": "Close video",
+  "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/te.json b/app/javascript/mastodon/locales/te.json
index bbe575fde..49f3ce2d9 100644
--- a/app/javascript/mastodon/locales/te.json
+++ b/app/javascript/mastodon/locales/te.json
@@ -7,14 +7,15 @@
   "account.disclaimer_full": "క్రింది సమాచారం వాడుకరి యొక్క ప్రొఫైల్ను అసంపూర్తిగా ప్రతిబింబించవచ్చు.",
   "account.domain_blocked": "డొమైన్ దాచిపెట్టబడినది",
   "account.edit_profile": "ప్రొఫైల్ని సవరించండి",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "ప్రొఫైల్లో చూపించు",
   "account.follow": "అనుసరించు",
   "account.followers": "అనుచరులు",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "ఈ వినియోగదారుడిని ఇంకా ఎవరూ అనుసరించడంలేదు.",
   "account.follows": "అనుసరిస్తున్నవి",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "ఈ వినియోగదారి ఇంకా ఎవరినీ అనుసరించడంలేదు.",
   "account.follows_you": "మిమ్మల్ని అనుసరిస్తున్నారు",
   "account.hide_reblogs": "@{name} నుంచి బూస్ట్ లను దాచిపెట్టు",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "మీడియా",
   "account.mention": "@{name}ను ప్రస్తావించు",
   "account.moved_to": "{name} ఇక్కడికి మారారు:",
@@ -29,7 +30,7 @@
   "account.show_reblogs": "@{name}నుంచి బూస్ట్ లను చూపించు",
   "account.unblock": "@{name}పై బ్లాక్ ను తొలగించు",
   "account.unblock_domain": "{domain}ను దాచవద్దు",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "ప్రొఫైల్లో చూపించవద్దు",
   "account.unfollow": "అనుసరించవద్దు",
   "account.unmute": "@{name}పై మ్యూట్ ని తొలగించు",
   "account.unmute_notifications": "@{name} నుంచి ప్రకటనలపై మ్యూట్ ని తొలగించు",
@@ -89,7 +90,7 @@
   "confirmations.mute.confirm": "మ్యూట్ చేయి",
   "confirmations.mute.message": "{name}ను మీరు ఖచ్చితంగా మ్యూట్ చేయాలనుకుంటున్నారా?",
   "confirmations.redraft.confirm": "తొలగించు & తిరగరాయు",
-  "confirmations.redraft.message": "మీరు ఖచ్చితంగా ఈ స్టేటస్ ని తొలగించి తిరగరాయాలనుకుంటున్నారా? మీరు అన్ని ప్రత్యుత్తరాలను, బూస్ట్ లను మరియు ఇష్టపడినవి కోల్పోతారు.",
+  "confirmations.redraft.message": "మీరు ఖచ్చితంగా ఈ స్టేటస్ ని తొలగించి తిరగరాయాలనుకుంటున్నారా? ఈ స్టేటస్ యొక్క బూస్ట్ లు మరియు ఇష్టాలు పోతాయి,మరియు ప్రత్యుత్తరాలు అనాధలు అయిపోతాయి.",
   "confirmations.unfollow.confirm": "అనుసరించవద్దు",
   "confirmations.unfollow.message": "{name}ను మీరు ఖచ్చితంగా అనుసరించవద్దనుకుంటున్నారా?",
   "embed.instructions": "దిగువ కోడ్ను కాపీ చేయడం ద్వారా మీ వెబ్సైట్లో ఈ స్టేటస్ ని పొందుపరచండి.",
@@ -108,19 +109,19 @@
   "emoji_button.search_results": "శోధన ఫలితాలు",
   "emoji_button.symbols": "చిహ్నాలు",
   "emoji_button.travel": "ప్రయాణం & ప్రదేశాలు",
-  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.blocks": "మీరు ఇంకా ఏ వినియోగదారులనూ బ్లాక్ చేయలేదు.",
   "empty_column.community": "స్థానిక కాలక్రమం ఖాళీగా ఉంది. మొదలుపెట్టడానికి బహిరంగంగా ఏదో ఒకటి వ్రాయండి!",
   "empty_column.direct": "మీకు ఇంకా ఏ ప్రత్యక్ష సందేశాలు లేవు. మీరు ఒకదాన్ని పంపినప్పుడు లేదా స్వీకరించినప్పుడు, అది ఇక్కడ చూపబడుతుంది.",
-  "empty_column.domain_blocks": "There are no hidden 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.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": "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.lists": "మీకు ఇంకా జాబితాలు ఏమీ లేవు. మీరు ఒకటి సృష్టించగానే, అది ఇక్కడ కనబడుతుంది.",
+  "empty_column.mutes": "మీరు ఇంకా ఏ వినియోగదారులనూ మ్యూట్ చేయలేదు.",
   "empty_column.notifications": "మీకు ఇంకా ఏ నోటిఫికేషన్లు లేవు. సంభాషణను ప్రారంభించడానికి ఇతరులతో ప్రతిస్పందించండి.",
   "empty_column.public": "ఇక్కడ ఏమీ లేదు! దీన్ని నింపడానికి బహిరంగంగా ఏదైనా వ్రాయండి, లేదా ఇతర దృష్టాంతాల్లోని వినియోగదారులను అనుసరించండి",
   "follow_request.authorize": "అనుమతించు",
@@ -137,32 +138,32 @@
   "home.column_settings.show_reblogs": "బూస్ట్ లను చూపించు",
   "home.column_settings.show_replies": "ప్రత్యుత్తరాలను చూపించు",
   "keyboard_shortcuts.back": "వెనక్కి తిరిగి వెళ్ళడానికి",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "బ్లాక్ చేయబడిన వినియోగదారుల జాబితాను తెరవడానికి",
   "keyboard_shortcuts.boost": "బూస్ట్ చేయడానికి",
   "keyboard_shortcuts.column": "నిలువు వరుసలలో ఒకదానిపై దృష్టి పెట్టడానికి",
   "keyboard_shortcuts.compose": "కంపోజ్ టెక్స్ట్ఏరియా పై దృష్టి పెట్టడానికి",
   "keyboard_shortcuts.description": "Description",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "నేరుగా పంపిన సందేశాల నిలువు వరుసను తెరువడానికి",
   "keyboard_shortcuts.down": "జాబితాలో క్రిందికి వెళ్ళడానికి",
   "keyboard_shortcuts.enter": "to open status",
   "keyboard_shortcuts.favourite": "ఇష్టపడడానికి",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "ఇష్టాల జాబితాను తెరవడానికి",
+  "keyboard_shortcuts.federated": "సమాఖ్య కాలక్రమాన్ని తెరవడానికి",
   "keyboard_shortcuts.heading": "కీబోర్డ్ సత్వరమార్గాలు",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "హోమ్ కాలక్రమాన్ని తెరవడానికి",
   "keyboard_shortcuts.hotkey": "హాట్ కీ",
   "keyboard_shortcuts.legend": "ఈ లెజెండ్ ప్రదర్శించడానికి",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "లోకల్ కాలక్రమాన్ని తెరవడానికి",
   "keyboard_shortcuts.mention": "రచయితను ప్రస్తావించడానికి",
-  "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.pinned": "to open pinned toots list",
+  "keyboard_shortcuts.muted": "మ్యూట్ చేయబడిన వినియోగదారుల జాబితాను తెరవడానికి",
+  "keyboard_shortcuts.my_profile": "మీ ప్రొఫైల్ను తెరవడానికి",
+  "keyboard_shortcuts.notifications": "నోటిఫికేషన్ల నిలువు వరుసను తెరవడానికి",
+  "keyboard_shortcuts.pinned": "అతికించబడిన టూట్ల జాబితాను తెరవడానికి",
   "keyboard_shortcuts.profile": "రచయిత ప్రొఫైల్ ను తెరవాలంటే",
   "keyboard_shortcuts.reply": "ప్రత్యుత్తరం ఇవ్వడానికి",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "ఫాలో రిక్వెస్ట్ల జాబితాను తెరవడానికి",
   "keyboard_shortcuts.search": "శోధనపై దృష్టి పెట్టండి",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
+  "keyboard_shortcuts.start": "\"ఇక్కడ ప్రారంభించండి\" నిలువు వరుసను తెరవడానికి",
   "keyboard_shortcuts.toggle_hidden": "CW వెనుక ఉన్న పాఠ్యాన్ని చూపడానికి / దాచడానికి",
   "keyboard_shortcuts.toot": "ఒక సరికొత్త టూట్ను ప్రారంభించడానికి",
   "keyboard_shortcuts.unfocus": "పాఠ్యం వ్రాసే ఏరియా/శోధన పట్టిక నుండి బయటకు రావడానికి",
@@ -183,10 +184,10 @@
   "missing_indicator.label": "దొరకలేదు",
   "missing_indicator.sublabel": "ఈ వనరు కనుగొనబడలేదు",
   "mute_modal.hide_notifications": "ఈ వినియోగదారు నుండి నోటిఫికేషన్లను దాచాలా?",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "మొబైల్ ఆప్ లు",
   "navigation_bar.blocks": "బ్లాక్ చేయబడిన వినియోగదారులు",
   "navigation_bar.community_timeline": "స్థానిక కాలక్రమం",
-  "navigation_bar.compose": "Compose new toot",
+  "navigation_bar.compose": "కొత్త టూట్ను రాయండి",
   "navigation_bar.direct": "ప్రత్యక్ష సందేశాలు",
   "navigation_bar.discover": "కనుగొను",
   "navigation_bar.domain_blocks": "దాచిన డొమైన్లు",
@@ -280,7 +281,7 @@
   "status.cancel_reblog_private": "బూస్ట్ను తొలగించు",
   "status.cannot_reblog": "ఈ పోస్ట్ను బూస్ట్ చేయడం సాధ్యం కాదు",
   "status.delete": "తొలగించు",
-  "status.detailed_status": "Detailed conversation view",
+  "status.detailed_status": "వివరణాత్మక సంభాషణ వీక్షణ",
   "status.direct": "@{name}కు నేరుగా సందేశం పంపు",
   "status.embed": "ఎంబెడ్",
   "status.favourite": "ఇష్టపడు",
@@ -297,7 +298,7 @@
   "status.reblog": "బూస్ట్",
   "status.reblog_private": "అసలు ప్రేక్షకులకు బూస్ట్ చేయి",
   "status.reblogged_by": "{name} బూస్ట్ చేసారు",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
+  "status.reblogs.empty": "ఈ టూట్ను ఇంకా ఎవరూ బూస్ట్ చేయలేదు. ఎవరైనా చేసినప్పుడు, అవి ఇక్కడ కనబడతాయి.",
   "status.redraft": "తొలగించు & తిరగరాయు",
   "status.reply": "ప్రత్యుత్తరం",
   "status.replyAll": "సంభాషణకు ప్రత్యుత్తరం ఇవ్వండి",
@@ -319,7 +320,7 @@
   "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} మాట్లాడుతున్నారు",
   "ui.beforeunload": "మీరు మాస్టొడొన్ను వదిలివేస్తే మీ డ్రాఫ్ట్లు పోతాయి.",
   "upload_area.title": "అప్లోడ్ చేయడానికి డ్రాగ్ & డ్రాప్ చేయండి",
-  "upload_button.label": "మీడియాను జోడించండి",
+  "upload_button.label": "మీడియాను జోడించండి (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_form.description": "దృష్టి లోపమున్న వారి కోసం వివరించండి",
   "upload_form.focus": "కత్తిరించు",
   "upload_form.undo": "తొలగించు",
diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json
index 5340fda92..2af5bfca1 100644
--- a/app/javascript/mastodon/locales/th.json
+++ b/app/javascript/mastodon/locales/th.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "Follows you",
   "account.hide_reblogs": "Hide boosts from @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Media",
   "account.mention": "Mention @{name}",
   "account.moved_to": "{name} has moved to:",
diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json
index b388b0b6f..68dc37e6b 100644
--- a/app/javascript/mastodon/locales/tr.json
+++ b/app/javascript/mastodon/locales/tr.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "Seni takip ediyor",
   "account.hide_reblogs": "Hide boosts from @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Media",
   "account.mention": "Bahset @{name}",
   "account.moved_to": "{name} has moved to:",
@@ -222,7 +223,7 @@
   "notifications.group": "{count} notifications",
   "onboarding.done": "Tamam",
   "onboarding.next": "Sıradaki",
-  "onboarding.page_five.public_timelines": "Yerel zaman tüneli, bu sunucudaki herkesten gelen gönderileri gösterir.Federe zaman tüneli, kullanıcıların diğer sunuculardan takip ettiği kişilerin herkese açık gönderilerini gösterir. Bunlar herkese açık zaman tünelleridir ve yeni insanlarla tanışmak  için harika yerlerdir. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new ",
+  "onboarding.page_five.public_timelines": "Yerel zaman tüneli, bu sunucudaki herkesten gelen gönderileri gösterir.Federe zaman tüneli, kullanıcıların diğer sunuculardan takip ettiği kişilerin herkese açık gönderilerini gösterir. Bunlar herkese açık zaman tünelleridir ve yeni insanlarla tanışmak  için harika yerlerdir. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new",
   "onboarding.page_four.home": "Takip ettiğiniz insanlardan gelen gönderileri gosteren zaman tünelidir",
   "onboarding.page_four.notifications": "Herkimse sizinle iletişime geçtiğinde gelen bildirimleri gösterir.",
   "onboarding.page_one.federation": "Mastodon, geniş bir sosyal ağ kurmak için birleşen bağımsız sunuculardan oluşan bir ağdır.",
diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json
index 9a2c18e36..5517abdf8 100644
--- a/app/javascript/mastodon/locales/uk.json
+++ b/app/javascript/mastodon/locales/uk.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "Підписаний(-а) на Вас",
   "account.hide_reblogs": "Сховати передмухи від @{name}",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "Медіа",
   "account.mention": "Згадати @{name}",
   "account.moved_to": "{name} переїхав на:",
diff --git a/app/javascript/mastodon/locales/whitelist_cy.json b/app/javascript/mastodon/locales/whitelist_cy.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_cy.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/mastodon/locales/whitelist_ro.json b/app/javascript/mastodon/locales/whitelist_ro.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_ro.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/mastodon/locales/whitelist_ta.json b/app/javascript/mastodon/locales/whitelist_ta.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_ta.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index cf2e1a640..254beab42 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "关注了你",
   "account.hide_reblogs": "隐藏来自 @{name} 的转嘟",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "媒体",
   "account.mention": "提及 @{name}",
   "account.moved_to": "{name} 已经迁移到:",
diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json
index dbad69191..ce141a8a7 100644
--- a/app/javascript/mastodon/locales/zh-HK.json
+++ b/app/javascript/mastodon/locales/zh-HK.json
@@ -15,6 +15,7 @@
   "account.follows.empty": "This user doesn't follow anyone yet.",
   "account.follows_you": "關注你",
   "account.hide_reblogs": "隱藏 @{name} 的轉推",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "媒體",
   "account.mention": "提及 @{name}",
   "account.moved_to": "{name} 已經遷移到:",
diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json
index 09e9150d0..fe4a81170 100644
--- a/app/javascript/mastodon/locales/zh-TW.json
+++ b/app/javascript/mastodon/locales/zh-TW.json
@@ -7,14 +7,15 @@
   "account.disclaimer_full": "下列資料不一定完整。",
   "account.domain_blocked": "站點被隱藏",
   "account.edit_profile": "編輯使用者資訊",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "在個人資訊頁面上推薦對方",
   "account.follow": "關注",
   "account.followers": "關注者",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "還沒有人關注這個使用者。",
   "account.follows": "正在關注",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "這個使用者還沒有關注任何人。",
   "account.follows_you": "關注你",
   "account.hide_reblogs": "隱藏來自 @{name} 的轉推",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.media": "媒體",
   "account.mention": "提到 @{name}",
   "account.moved_to": "{name} 已經移至:",
@@ -29,7 +30,7 @@
   "account.show_reblogs": "顯示來自 @{name} 的嘟文",
   "account.unblock": "取消封鎖 @{name}",
   "account.unblock_domain": "不再隱藏 {domain}",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "不再於個人資訊頁面上推薦對方",
   "account.unfollow": "取消關注",
   "account.unmute": "不再靜音 @{name}",
   "account.unmute_notifications": "不再對來自 @{name} 的通知靜音",
@@ -89,7 +90,7 @@
   "confirmations.mute.confirm": "消音",
   "confirmations.mute.message": "你確定要消音 {name} ?",
   "confirmations.redraft.confirm": "刪除 & 編輯",
-  "confirmations.redraft.message": "你確定要刪除這條嘟文並重新編輯它嗎? 所有相關的回覆、轉嘟與最愛都會被刪除。",
+  "confirmations.redraft.message": "你確定要刪除這條嘟文並重新編輯它嗎?所有相關的轉嘟與最愛都會被刪除,而對原始嘟文的回覆將會變成孤兒。",
   "confirmations.unfollow.confirm": "取消關注",
   "confirmations.unfollow.message": "真的不要繼續關注 {name} 了嗎?",
   "embed.instructions": "要內嵌此嘟文,請將以下代碼貼進你的網站。",
@@ -108,25 +109,25 @@
   "emoji_button.search_results": "搜尋結果",
   "emoji_button.symbols": "符號",
   "emoji_button.travel": "旅遊與地點",
-  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.blocks": "你還沒有封鎖任何使用者。",
   "empty_column.community": "本地時間軸是空的。公開寫點什麼吧!",
   "empty_column.direct": "你還沒有使用過私訊。當你發出或著收到私訊時,它會在這裡顯示。",
-  "empty_column.domain_blocks": "There are no hidden 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.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": "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.list": "此份名單尚未有東西。當此名單的成員嘟出了新的狀態時,它們就會出現在這裡。",
+  "empty_column.lists": "你還沒有建立任何名單。你建立的名單將會顯示在這裡。",
+  "empty_column.mutes": "你還沒有靜音任何使用者。",
   "empty_column.notifications": "還沒有任何通知。和別的使用者互動來開始對話。",
   "empty_column.public": "這裡什麼都沒有! 寫一些公開的嘟文,或著關注其他站點的使用者後,這裡就會有嘟文出現了",
   "follow_request.authorize": "授權",
   "follow_request.reject": "拒絕",
   "getting_started.developers": "開發",
-  "getting_started.documentation": "Documentation",
+  "getting_started.documentation": "文件",
   "getting_started.find_friends": "尋找 Twitter 好友",
   "getting_started.heading": "馬上開始",
   "getting_started.invite": "邀請使用者",
@@ -137,32 +138,32 @@
   "home.column_settings.show_reblogs": "顯示轉推",
   "home.column_settings.show_replies": "顯示回應",
   "keyboard_shortcuts.back": "回到上一個",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "到封鎖的使用者名單",
   "keyboard_shortcuts.boost": "到轉推",
   "keyboard_shortcuts.column": "選擇第 X 欄中的嘟文",
   "keyboard_shortcuts.compose": "焦點移至撰寫文字區塊",
   "keyboard_shortcuts.description": "描述",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "到私訊欄",
   "keyboard_shortcuts.down": "在列表往下移動",
-  "keyboard_shortcuts.enter": "to open status",
+  "keyboard_shortcuts.enter": "看嘟文",
   "keyboard_shortcuts.favourite": "收藏",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "到收藏名單",
+  "keyboard_shortcuts.federated": "到其他站點時間軸",
   "keyboard_shortcuts.heading": "鍵盤快速鍵",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "到主頁時間軸",
   "keyboard_shortcuts.hotkey": "快速鍵",
   "keyboard_shortcuts.legend": "顯示這個說明",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "到本地時間軸",
   "keyboard_shortcuts.mention": "到提到的作者",
-  "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.pinned": "to open pinned toots list",
-  "keyboard_shortcuts.profile": "to open author's profile",
+  "keyboard_shortcuts.muted": "到靜音的使用者列表",
+  "keyboard_shortcuts.my_profile": "到你的個人資訊頁",
+  "keyboard_shortcuts.notifications": "打開通知欄",
+  "keyboard_shortcuts.pinned": "到收藏的嘟文名單",
+  "keyboard_shortcuts.profile": "到嘟文作者的個人資訊頁",
   "keyboard_shortcuts.reply": "到回應",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "打開關注請求名單",
   "keyboard_shortcuts.search": "把滑鼠移動到搜尋",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
+  "keyboard_shortcuts.start": "到「馬上開始」",
   "keyboard_shortcuts.toggle_hidden": "顯示或隱藏被標為敏感的嘟文",
   "keyboard_shortcuts.toot": "新的嘟文",
   "keyboard_shortcuts.unfocus": "取消輸入",
@@ -183,16 +184,16 @@
   "missing_indicator.label": "找不到",
   "missing_indicator.sublabel": "找不到此資源",
   "mute_modal.hide_notifications": "隱藏來自這個使用者的通知?",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "行動應用程式",
   "navigation_bar.blocks": "封鎖的使用者",
   "navigation_bar.community_timeline": "本地時間軸",
-  "navigation_bar.compose": "Compose new toot",
+  "navigation_bar.compose": "寫新的嘟文",
   "navigation_bar.direct": "私訊",
   "navigation_bar.discover": "探索",
   "navigation_bar.domain_blocks": "隱藏的站點",
   "navigation_bar.edit_profile": "編輯使用者資訊",
   "navigation_bar.favourites": "最愛",
-  "navigation_bar.filters": "Muted words",
+  "navigation_bar.filters": "消音的詞",
   "navigation_bar.follow_requests": "關注請求",
   "navigation_bar.info": "關於本站",
   "navigation_bar.keyboard_shortcuts": "快捷鍵",
@@ -280,11 +281,11 @@
   "status.cancel_reblog_private": "取消轉嘟",
   "status.cannot_reblog": "這篇嘟文無法被轉嘟",
   "status.delete": "刪除",
-  "status.detailed_status": "Detailed conversation view",
+  "status.detailed_status": "對話的詳細內容",
   "status.direct": "發送私訊給 @{name}",
   "status.embed": "嵌入",
   "status.favourite": "最愛",
-  "status.filtered": "Filtered",
+  "status.filtered": "已過濾",
   "status.load_more": "載入更多",
   "status.media_hidden": "隱藏媒體內容",
   "status.mention": "提到 @{name}",
@@ -297,7 +298,7 @@
   "status.reblog": "轉嘟",
   "status.reblog_private": "轉嘟給原有關注者",
   "status.reblogged_by": "{name} 轉嘟了",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
+  "status.reblogs.empty": "還沒有人轉嘟。如果有,會顯示在這裡。",
   "status.redraft": "刪除 & 編輯",
   "status.reply": "回覆",
   "status.replyAll": "回覆所有人",
@@ -319,7 +320,7 @@
   "trends.count_by_accounts": "{count} 位使用者在討論",
   "ui.beforeunload": "如果離開 Mastodon,你的草稿將會不見。",
   "upload_area.title": "拖放來上傳",
-  "upload_button.label": "上傳媒體檔案",
+  "upload_button.label": "上傳媒體檔案 (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_form.description": "為視障人士增加文字說明",
   "upload_form.focus": "裁切",
   "upload_form.undo": "刪除",
diff --git a/app/javascript/mastodon/reducers/dropdown_menu.js b/app/javascript/mastodon/reducers/dropdown_menu.js
index 5449884cc..36fd4f132 100644
--- a/app/javascript/mastodon/reducers/dropdown_menu.js
+++ b/app/javascript/mastodon/reducers/dropdown_menu.js
@@ -4,12 +4,12 @@ import {
   DROPDOWN_MENU_CLOSE,
 } from '../actions/dropdown_menu';
 
-const initialState = Immutable.Map({ openId: null, placement: null });
+const initialState = Immutable.Map({ openId: null, placement: null, keyboard: false });
 
 export default function dropdownMenu(state = initialState, action) {
   switch (action.type) {
   case DROPDOWN_MENU_OPEN:
-    return state.merge({ openId: action.id, placement: action.placement });
+    return state.merge({ openId: action.id, placement: action.placement, keyboard: action.keyboard });
   case DROPDOWN_MENU_CLOSE:
     return state.get('openId') === action.id ? state.set('openId', null) : state;
   default:
diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js
index 1cb491e17..a62974ec0 100644
--- a/app/javascript/packs/public.js
+++ b/app/javascript/packs/public.js
@@ -52,6 +52,7 @@ function main() {
     });
 
     const reactComponents = document.querySelectorAll('[data-component]');
+
     if (reactComponents.length > 0) {
       import(/* webpackChunkName: "containers/media_container" */ '../mastodon/containers/media_container')
         .then(({ default: MediaContainer }) => {
@@ -64,6 +65,7 @@ function main() {
     }
 
     const parallaxComponents = document.querySelectorAll('.parallax');
+
     if (parallaxComponents.length > 0 ) {
       new Rellax('.parallax', { speed: -1 });
     }
@@ -71,6 +73,7 @@ function main() {
     const history = createHistory();
     const detailedStatuses = document.querySelectorAll('.public-layout .detailed-status');
     const location = history.location;
+
     if (detailedStatuses.length === 1 && (!location.state || !location.state.scrolledToDetailedStatus)) {
       detailedStatuses[0].scrollIntoView();
       history.replace(location.pathname, { ...location.state, scrolledToDetailedStatus: true });
diff --git a/app/javascript/styles/mastodon/accessibility.scss b/app/javascript/styles/mastodon/accessibility.scss
index 373bcd4ac..d33806c84 100644
--- a/app/javascript/styles/mastodon/accessibility.scss
+++ b/app/javascript/styles/mastodon/accessibility.scss
@@ -1,4 +1,4 @@
-$black-emojis: '8ball' 'ant' 'back' 'black_circle' 'black_large_square' 'black_medium_small_square' 'black_medium_square' 'black_nib' 'black_small_square' 'bomb' 'bust_in_silhouette' 'camera' 'camera_with_flash' 'clubs' 'copyright' 'curly_loop' 'currency_exchange' 'end' 'heavy_check_mark' 'heavy_division_sign' 'heavy_dollar_sign' 'heavy_minus_sign' 'heavy_multiplication_x' 'heavy_plus_sign' 'lower_left_fountain_pen' 'on' 'registered' 'soon' 'spades' 'spider' 'tm' 'top' 'waving_black_flag' 'wavy_dash' 'video_game';
+$black-emojis: '8ball' 'ant' 'back' 'black_circle' 'black_heart' 'black_large_square' 'black_medium_small_square' 'black_medium_square' 'black_nib' 'black_small_square' 'bomb' 'bowling' 'bust_in_silhouette' 'busts_in_silhouette' 'camera' 'camera_with_flash' 'clubs' 'copyright' 'curly_loop' 'currency_exchange' 'dark_sunglasses' 'eight_pointed_black_star' 'electric_plug' 'end' 'female-guard' 'film_projector' 'fried_egg' 'gorilla' 'guardsman' 'heavy_check_mark' 'heavy_division_sign' 'heavy_dollar_sign' 'heavy_minus_sign' 'heavy_multiplication_x' 'heavy_plus_sign' 'hocho' 'hole' 'joystick' 'kaaba' 'lower_left_ballpoint_pen' 'lower_left_fountain_pen' 'male-guard' 'microphone' 'mortar_board' 'movie_camera' 'musical_score' 'on' 'registered' 'soon' 'spades' 'speaking_head_in_silhouette' 'spider' 'telephone_receiver' 'tm' 'top' 'tophat' 'turkey' 'vhs' 'video_camera' 'video_game' 'water_buffalo' 'waving_black_flag' 'wavy_dash';
 
 %white-emoji-outline {
   filter: drop-shadow(1px 1px 0 $white) drop-shadow(-1px 1px 0 $white) drop-shadow(1px -1px 0 $white) drop-shadow(-1px -1px 0 $white);
diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss
index c27bc0df3..06effbdb2 100644
--- a/app/javascript/styles/mastodon/accounts.scss
+++ b/app/javascript/styles/mastodon/accounts.scss
@@ -265,6 +265,20 @@
     }
   }
 
+  .verified {
+    border: 1px solid rgba($valid-value-color, 0.5);
+    background: rgba($valid-value-color, 0.25);
+
+    a {
+      color: $valid-value-color;
+      font-weight: 500;
+    }
+
+    &__mark {
+      color: $valid-value-color;
+    }
+  }
+
   dl:last-child {
     border-bottom: 0;
   }
diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss
index 42f507296..9dfd89dc2 100644
--- a/app/javascript/styles/mastodon/admin.scss
+++ b/app/javascript/styles/mastodon/admin.scss
@@ -1,3 +1,5 @@
+$no-columns-breakpoint: 600px;
+
 .admin-wrapper {
   display: flex;
   justify-content: center;
@@ -24,12 +26,22 @@
       height: 100px;
     }
 
+    @media screen and (max-width: $no-columns-breakpoint) {
+      & > a:first-child {
+        display: none;
+      }
+    }
+
     ul {
       list-style: none;
       border-radius: 4px 0 0 4px;
       overflow: hidden;
       margin-bottom: 20px;
 
+      @media screen and (max-width: $no-columns-breakpoint) {
+        margin-bottom: 0;
+      }
+
       a {
         display: block;
         padding: 15px;
@@ -62,20 +74,24 @@
         a {
           border: 0;
           padding: 15px 35px;
+        }
+      }
 
-          &.selected {
-            color: $primary-text-color;
-            background-color: $ui-highlight-color;
-            border-bottom: 0;
-            border-radius: 0;
+      .simple-navigation-active-leaf a {
+        color: $primary-text-color;
+        background-color: $ui-highlight-color;
+        border-bottom: 0;
+        border-radius: 0;
 
-            &:hover {
-              background-color: lighten($ui-highlight-color, 5%);
-            }
-          }
+        &:hover {
+          background-color: lighten($ui-highlight-color, 5%);
         }
       }
     }
+
+    & > ul > .simple-navigation-active-leaf a {
+      border-radius: 4px 0 0 4px;
+    }
   }
 
   .content-wrapper {
@@ -89,11 +105,19 @@
     padding-top: 60px;
     padding-left: 25px;
 
+    @media screen and (max-width: $no-columns-breakpoint) {
+      max-width: none;
+      padding: 15px;
+      padding-top: 30px;
+    }
+
     h2 {
       color: $secondary-text-color;
       font-size: 24px;
       line-height: 28px;
       font-weight: 400;
+      padding-bottom: 40px;
+      border-bottom: 1px solid lighten($ui-base-color, 8%);
       margin-bottom: 40px;
     }
 
@@ -108,7 +132,7 @@
     h4 {
       text-transform: uppercase;
       font-size: 13px;
-      font-weight: 500;
+      font-weight: 700;
       color: $darker-text-color;
       padding-bottom: 8px;
       margin-bottom: 8px;
@@ -122,6 +146,11 @@
       font-weight: 400;
     }
 
+    .fields-group h6 {
+      color: $primary-text-color;
+      font-weight: 500;
+    }
+
     & > p {
       font-size: 14px;
       line-height: 18px;
@@ -172,30 +201,7 @@
     }
   }
 
-  .simple_form {
-    max-width: 400px;
-
-    &.edit_user,
-    &.new_form_admin_settings,
-    &.new_form_two_factor_confirmation,
-    &.new_form_delete_confirmation,
-    &.new_import,
-    &.new_domain_block,
-    &.edit_domain_block {
-      max-width: none;
-    }
-
-    .form_two_factor_confirmation_code,
-    .form_delete_confirmation_password {
-      max-width: 400px;
-    }
-
-    .actions {
-      max-width: 400px;
-    }
-  }
-
-  @media screen and (max-width: 600px) {
+  @media screen and (max-width: $no-columns-breakpoint) {
     display: block;
     overflow-y: auto;
     -webkit-overflow-scrolling: touch;
@@ -209,16 +215,8 @@
 
     .sidebar {
       width: 100%;
-      padding: 10px 0;
+      padding: 0;
       height: auto;
-
-      .logo {
-        margin: 20px auto;
-      }
-    }
-
-    .content {
-      padding-top: 20px;
     }
   }
 }
@@ -567,3 +565,102 @@ a.name-tag,
     color: $dark-text-color;
   }
 }
+
+.report-card {
+  background: $ui-base-color;
+  border-radius: 4px;
+  margin-bottom: 20px;
+
+  &__profile {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 15px;
+
+    .account {
+      padding: 0;
+      border: 0;
+
+      &__avatar-wrapper {
+        margin-left: 0;
+      }
+    }
+
+    &__stats {
+      flex: 0 0 auto;
+      font-weight: 500;
+      color: $darker-text-color;
+      text-transform: uppercase;
+      text-align: right;
+
+      a {
+        color: inherit;
+        text-decoration: none;
+
+        &:focus,
+        &:hover,
+        &:active {
+          color: lighten($darker-text-color, 8%);
+        }
+      }
+
+      .red {
+        color: $error-value-color;
+      }
+    }
+  }
+
+  &__summary {
+    &__item {
+      display: flex;
+      justify-content: flex-start;
+      border-top: 1px solid darken($ui-base-color, 4%);
+
+      &:hover {
+        background: lighten($ui-base-color, 2%);
+      }
+
+      &__reported-by,
+      &__assigned {
+        padding: 15px;
+        flex: 0 0 auto;
+        box-sizing: border-box;
+        width: 150px;
+        color: $darker-text-color;
+
+        &,
+        .username {
+          white-space: nowrap;
+          overflow: hidden;
+          text-overflow: ellipsis;
+        }
+      }
+
+      &__content {
+        flex: 1 1 auto;
+        max-width: calc(100% - 300px);
+
+        &__icon {
+          color: $dark-text-color;
+          margin-right: 4px;
+          font-weight: 500;
+        }
+      }
+
+      &__content a {
+        display: block;
+        box-sizing: border-box;
+        width: 100%;
+        padding: 15px;
+        text-decoration: none;
+        color: $darker-text-color;
+      }
+    }
+  }
+}
+
+.one-line {
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss
index 7a6a1c490..3bbb31e6e 100644
--- a/app/javascript/styles/mastodon/basics.scss
+++ b/app/javascript/styles/mastodon/basics.scss
@@ -1,3 +1,10 @@
+@function hex-color($color) {
+  @if type-of($color) == 'color' {
+    $color: str-slice(ie-hex-str($color), 4);
+  }
+  @return '%23' + unquote($color)
+}
+
 body {
   font-family: 'mastodon-font-sans-serif', sans-serif;
   background: darken($ui-base-color, 8%);
diff --git a/app/javascript/styles/mastodon/boost.scss b/app/javascript/styles/mastodon/boost.scss
index 8e11cb596..5a6d6ae40 100644
--- a/app/javascript/styles/mastodon/boost.scss
+++ b/app/javascript/styles/mastodon/boost.scss
@@ -1,10 +1,3 @@
-@function hex-color($color) {
-  @if type-of($color) == 'color' {
-    $color: str-slice(ie-hex-str($color), 4);
-  }
-  @return '%23' + unquote($color)
-}
-
 button.icon-button i.fa-retweet {
   background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='209'><path d='M4.97 3.16c-.1.03-.17.1-.22.18L.8 8.24c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77L5.5 3.35c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.02-2.4.02H7.1l2.32 2.85.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($action-button-color)}' stroke-width='0'/><path d='M7.78 19.66c-.24.02-.44.25-.44.5v2.46h-.06c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v4.47c0 4.26-.56 3.62 3.65 3.62H8.5l-1.3-1.06c-.1-.08-.18-.2-.2-.3-.02-.17.06-.35.2-.45l1.33-1.1H7.28c-.44 0-.72-.3-.72-.7v-4.48c0-.44.28-.72.72-.72h.06v2.5c0 .38.54.63.82.38l4.9-3.93c.25-.18.25-.6 0-.78l-4.9-3.92c-.1-.1-.24-.14-.38-.12zm9.34 2.93c-.54-.02-1.3.02-2.4.02h-1.25l1.3 1.07c.1.07.18.2.2.33.02.16-.06.3-.2.4l-1.33 1.1h1.28c.42 0 .72.28.72.72v4.47c0 .42-.3.72-.72.72h-.1v-2.47c0-.3-.3-.53-.6-.47-.07 0-.14.05-.2.1l-4.9 3.93c-.26.18-.26.6 0 .78l4.9 3.92c.27.25.82 0 .8-.38v-2.5h.1c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.15.4-3.62-1.25-3.66zM10.34 38.66c-.24.02-.44.25-.43.5v2.47H7.3c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.47c0 3.66-.23 3.7 2.34 3.66l-1.34-1.1c-.1-.08-.18-.2-.2-.3 0-.17.07-.35.2-.45l1.96-1.6c-.03-.06-.04-.13-.04-.2v-4.48c0-.44.28-.72.72-.72H9.9v2.5c0 .36.5.6.8.38l4.93-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.08-.23-.13-.36-.12zm5.63 2.93l1.34 1.1c.1.07.18.2.2.33.02.16-.03.3-.16.4l-1.96 1.6c.02.07.06.13.06.22v4.47c0 .42-.3.72-.72.72h-2.66v-2.47c0-.3-.3-.53-.6-.47-.06.02-.12.05-.18.1l-4.94 3.93c-.24.18-.24.6 0 .78l4.94 3.92c.28.22.78-.02.78-.38v-2.5h2.66c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.66.34-3.7-2.4-3.66zM13.06 57.66c-.23.03-.4.26-.4.5v2.47H7.28c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.87l2.93-2.37v-2.5c0-.44.28-.72.72-.72h5.38v2.5c0 .36.5.6.78.38l4.94-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.1-.24-.14-.38-.12zm5.3 6.15l-2.92 2.4v2.52c0 .42-.3.72-.72.72h-5.4v-2.47c0-.3-.32-.53-.6-.47-.07.02-.13.05-.2.1L3.6 70.52c-.25.18-.25.6 0 .78l4.93 3.92c.28.22.78-.02.78-.38v-2.5h5.42c4.27 0 3.65.67 3.65-3.62v-4.47-.44zM19.25 78.8c-.1.03-.2.1-.28.17l-.9.9c-.44-.3-1.36-.25-3.35-.25H7.28c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v.7l2.93.3v-1c0-.44.28-.72.72-.72h7.44c.2 0 .37.08.5.2l-1.8 1.8c-.25.26-.08.76.27.8l6.27.7c.28.03.56-.25.53-.53l-.7-6.25c0-.27-.3-.48-.55-.44zm-17.2 6.1c-.2.07-.36.3-.33.54l.7 6.25c.02.36.58.55.83.27l.8-.8c.02 0 .04-.02.04 0 .46.24 1.37.17 3.18.17h7.44c4.27 0 3.65.67 3.65-3.62v-.75l-2.93-.3v1.05c0 .42-.3.72-.72.72H7.28c-.15 0-.3-.03-.4-.1L8.8 86.4c.3-.24.1-.8-.27-.84l-6.28-.65h-.2zM4.88 98.6c-1.33 0-1.34.48-1.3 2.3l1.14-1.37c.08-.1.22-.17.34-.2.16 0 .34.08.44.2l1.66 2.03c.04 0 .07-.03.12-.03h7.44c.34 0 .57.2.65.5h-2.43c-.34.05-.53.52-.3.78l3.92 4.95c.18.24.6.24.78 0l3.94-4.94c.22-.27-.02-.76-.37-.77H18.4c.02-3.9.6-3.4-3.66-3.4H7.28c-1.08 0-1.86-.04-2.4-.04zm.15 2.46c-.1.03-.2.1-.28.2l-3.94 4.9c-.2.28.03.77.4.78H3.6c-.02 3.94-.45 3.4 3.66 3.4h7.44c3.65 0 3.74.3 3.7-2.25l-1.1 1.34c-.1.1-.2.17-.32.2-.16 0-.34-.08-.44-.2l-1.65-2.03c-.06.02-.1.04-.18.04H7.28c-.35 0-.57-.2-.66-.5h2.44c.37 0 .63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.23-.47-.2zM4.88 117.6c-1.16 0-1.3.3-1.3 1.56l1.14-1.38c.08-.1.22-.14.34-.16.16 0 .34.04.44.16l2.22 2.75h7c.42 0 .72.28.72.72v.53h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-.53c0-4.2.72-3.63-3.66-3.63H7.28c-1.08 0-1.86-.03-2.4-.03zm.1 1.74c-.1.03-.17.1-.23.16L.8 124.44c-.2.28.03.77.4.78H3.6v.5c0 4.26-.55 3.62 3.66 3.62h7.44c1.03 0 1.74.02 2.28 0-.16.02-.34-.03-.44-.15l-2.22-2.76H7.28c-.44 0-.72-.3-.72-.72v-.5h2.5c.37.02.63-.5.4-.78L5.5 119.5c-.12-.15-.34-.22-.53-.16zm12.02 10c1.2-.02 1.4-.25 1.4-1.53l-1.1 1.36c-.07.1-.17.17-.3.18zM5.94 136.6l2.37 2.93h6.42c.42 0 .72.28.72.72v1.25h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.25c0-4.2.72-3.63-3.66-3.63H7.28c-.6 0-.92-.02-1.34-.03zm-1.72.06c-.4.08-.54.3-.6.75l.6-.74zm.84.93c-.12 0-.24.08-.3.18l-3.95 4.9c-.24.3 0 .83.4.82H3.6v1.22c0 4.26-.55 3.62 3.66 3.62h7.44c.63 0 .97.02 1.4.03l-2.37-2.93H7.28c-.44 0-.72-.3-.72-.72v-1.22h2.5c.4.04.67-.53.4-.8l-3.96-4.92c-.1-.13-.27-.2-.44-.2zm13.28 10.03l-.56.7c.36-.07.5-.3.56-.7zM17.13 155.6c-.55-.02-1.32.03-2.4.03h-8.2l2.38 2.9h5.82c.42 0 .72.28.72.72v1.97H12.9c-.32.06-.48.52-.28.78l3.94 4.94c.2.23.6.22.78-.03l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.97c0-3.15.4-3.62-1.25-3.66zm-12.1.28c-.1.02-.2.1-.28.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v1.96c0 4.26-.55 3.62 3.66 3.62h8.24l-2.36-2.9H7.28c-.44 0-.72-.3-.72-.72v-1.97h2.5c.37.02.63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.22-.47-.2zM5.13 174.5c-.15 0-.3.07-.38.2L.8 179.6c-.24.27 0 .82.4.8H3.6v2.32c0 4.26-.55 3.62 3.66 3.62h7.94l-2.35-2.9h-5.6c-.43 0-.7-.3-.7-.72v-2.3h2.5c.38.03.66-.54.4-.83l-3.97-4.9c-.1-.13-.23-.2-.38-.2zm12 .1c-.55-.02-1.32.03-2.4.03H6.83l2.35 2.9h5.52c.42 0 .72.28.72.72v2.34h-2.6c-.3.1-.43.53-.2.78l3.92 4.9c.18.24.6.24.78 0l3.94-4.9c.22-.3-.02-.78-.37-.8H18.4v-2.33c0-3.15.4-3.62-1.25-3.66zM4.97 193.16c-.1.03-.17.1-.22.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77l-3.96-4.9c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.03-2.4.03H7.1l2.32 2.84.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($highlight-text-color)}' stroke-width='0'/></svg>");
 
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 8f279e138..490e26ccc 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -1297,6 +1297,11 @@ a .account__avatar {
   flex: 0 1 100%;
   border-right: 1px solid lighten($ui-base-color, 8%);
   padding: 10px 0;
+  border-bottom: 4px solid transparent;
+
+  &.active {
+    border-bottom: 4px solid $ui-highlight-color;
+  }
 
   & > span {
     display: block;
@@ -1666,14 +1671,14 @@ a.account__display-name {
 
   &.top {
     bottom: -5px;
-    margin-left: -13px;
+    margin-left: -7px;
     border-width: 5px 7px 0;
     border-top-color: $ui-secondary-color;
   }
 
   &.bottom {
     top: -5px;
-    margin-left: -13px;
+    margin-left: -7px;
     border-width: 0 7px 5px;
     border-bottom-color: $ui-secondary-color;
   }
@@ -2279,28 +2284,6 @@ a.account__display-name {
 .getting-started {
   color: $dark-text-color;
 
-  p {
-    color: $dark-text-color;
-    font-size: 13px;
-    margin-bottom: 20px;
-
-    a {
-      color: $dark-text-color;
-      text-decoration: underline;
-    }
-  }
-
-  a {
-    text-decoration: none;
-    color: $darker-text-color;
-
-    &:hover,
-    &:focus,
-    &:active {
-      text-decoration: underline;
-    }
-  }
-
   &__footer {
     flex: 0 0 auto;
     padding: 10px;
@@ -2313,6 +2296,28 @@ a.account__display-name {
     ul li {
       display: inline;
     }
+
+    p {
+      color: $dark-text-color;
+      font-size: 13px;
+      margin-bottom: 20px;
+
+      a {
+        color: $dark-text-color;
+        text-decoration: underline;
+      }
+    }
+
+    a {
+      text-decoration: none;
+      color: $darker-text-color;
+
+      &:hover,
+      &:focus,
+      &:active {
+        text-decoration: underline;
+      }
+    }
   }
 
   &__trends {
@@ -5358,9 +5363,11 @@ noscript {
   overflow: hidden;
   margin: 20px -10px -20px;
   border-bottom: 0;
+  border-top: 0;
 
   dl {
-    border-top: 1px solid lighten($ui-base-color, 8%);
+    border-top: 1px solid lighten($ui-base-color, 4%);
+    border-bottom: 0;
     display: flex;
   }
 
@@ -5387,6 +5394,11 @@ noscript {
     flex: 1 1 auto;
     color: $primary-text-color;
     background: $ui-base-color;
+
+    &.verified {
+      border: 1px solid rgba($valid-value-color, 0.5);
+      background: rgba($valid-value-color, 0.25);
+    }
   }
 }
 
diff --git a/app/javascript/styles/mastodon/containers.scss b/app/javascript/styles/mastodon/containers.scss
index 8ecedd2cb..47582f323 100644
--- a/app/javascript/styles/mastodon/containers.scss
+++ b/app/javascript/styles/mastodon/containers.scss
@@ -345,6 +345,23 @@
     margin-bottom: 10px;
     box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
 
+    &.inactive {
+      opacity: 0.5;
+
+      .public-account-header__image,
+      .avatar {
+        filter: grayscale(100%);
+      }
+
+      .logo-button {
+        background-color: $secondary-text-color;
+
+        svg path:last-child {
+          fill: $secondary-text-color;
+        }
+      }
+    }
+
     &__image {
       border-radius: 4px 4px 0 0;
       overflow: hidden;
@@ -582,6 +599,10 @@
               border-bottom: 4px solid $highlight-text-color;
               opacity: 1;
             }
+
+            &.inactive::after {
+              border-bottom-color: $secondary-text-color;
+            }
           }
 
           &:hover {
@@ -697,6 +718,14 @@
       a {
         color: lighten($ui-highlight-color, 8%);
       }
+
+      dl:first-child .verified {
+        border-radius: 0 4px 0 0;
+      }
+
+      .verified a {
+        color: $valid-value-color;
+      }
     }
 
     .account__header__content {
diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss
index 144b4a519..cbd3de94c 100644
--- a/app/javascript/styles/mastodon/forms.scss
+++ b/app/javascript/styles/mastodon/forms.scss
@@ -1,3 +1,5 @@
+$no-columns-breakpoint: 600px;
+
 code {
   font-family: 'mastodon-font-monospace', monospace;
   font-weight: 400;
@@ -13,6 +15,60 @@ code {
   .input {
     margin-bottom: 15px;
     overflow: hidden;
+
+    &.hidden {
+      margin: 0;
+    }
+
+    &.radio_buttons {
+      .radio {
+        margin-bottom: 15px;
+
+        &:last-child {
+          margin-bottom: 0;
+        }
+      }
+
+      .radio > label {
+        position: relative;
+        padding-left: 28px;
+
+        input {
+          position: absolute;
+          top: -2px;
+          left: 0;
+        }
+      }
+    }
+
+    &.boolean {
+      position: relative;
+      margin-bottom: 0;
+
+      .label_input > label {
+        font-family: inherit;
+        font-size: 14px;
+        padding-top: 5px;
+        color: $primary-text-color;
+        display: block;
+        width: auto;
+      }
+
+      .label_input,
+      .hint {
+        padding-left: 28px;
+      }
+
+      .label_input__wrapper {
+        position: static;
+      }
+
+      label.checkbox {
+        position: absolute;
+        top: 2px;
+        left: 0;
+      }
+    }
   }
 
   .row {
@@ -27,9 +83,22 @@ code {
     }
   }
 
+  .hint {
+    color: $darker-text-color;
+
+    a {
+      color: $highlight-text-color;
+    }
+
+    code {
+      border-radius: 3px;
+      padding: 0.2em 0.4em;
+      background: darken($ui-base-color, 12%);
+    }
+  }
+
   span.hint {
     display: block;
-    color: $darker-text-color;
     font-size: 12px;
     margin-top: 4px;
   }
@@ -44,17 +113,6 @@ code {
       line-height: 18px;
       margin-top: 15px;
       margin-bottom: 0;
-      color: $darker-text-color;
-
-      a {
-        color: $highlight-text-color;
-      }
-    }
-
-    code {
-      border-radius: 3px;
-      padding: 0.2em 0.4em;
-      background: darken($ui-base-color, 12%);
     }
   }
 
@@ -72,87 +130,60 @@ code {
     }
   }
 
-  .label_input {
-    display: flex;
+  .input.with_floating_label {
+    .label_input {
+      display: flex;
 
-    label {
-      flex: 0 0 auto;
+      & > label {
+        font-family: inherit;
+        font-size: 14px;
+        color: $primary-text-color;
+        font-weight: 500;
+        min-width: 150px;
+        flex: 0 0 auto;
+      }
+
+      input,
+      select {
+        flex: 1 1 auto;
+      }
     }
 
-    input {
-      flex: 1 1 auto;
+    &.select .hint {
+      margin-top: 6px;
+      margin-left: 150px;
     }
   }
 
   .input.with_label {
-    padding: 15px 0;
-    margin-bottom: 0;
-
-    .label_input {
-      flex-wrap: wrap;
-      align-items: flex-start;
-    }
-
-    &.file .label_input {
-      flex-wrap: nowrap;
-    }
-
-    &.select .label_input {
-      align-items: initial;
-    }
-
     .label_input > label {
       font-family: inherit;
-      font-size: 16px;
+      font-size: 14px;
       color: $primary-text-color;
       display: block;
-      padding-top: 5px;
-      margin-bottom: 5px;
-      flex: 1;
-      min-width: 150px;
+      margin-bottom: 8px;
       word-wrap: break-word;
+      font-weight: 500;
+    }
 
-      &.select {
-        flex: 0;
-      }
-
-      & ~ * {
-        margin-left: 10px;
-      }
+    .hint {
+      margin-top: 6px;
     }
 
     ul {
       flex: 390px;
     }
-
-    &.boolean {
-      padding: initial;
-      margin-bottom: initial;
-
-      .label_input > label {
-        font-family: inherit;
-        font-size: 14px;
-        color: $primary-text-color;
-        display: block;
-        width: auto;
-      }
-
-      label.checkbox {
-        position: relative;
-        padding-left: 25px;
-        flex: 1 1 auto;
-      }
-    }
   }
 
   .input.with_block_label {
-    padding-top: 15px;
+    max-width: none;
 
     & > label {
       font-family: inherit;
       font-size: 16px;
       color: $primary-text-color;
       display: block;
+      font-weight: 500;
       padding-top: 5px;
     }
 
@@ -165,49 +196,70 @@ code {
     }
   }
 
+  .required abbr {
+    text-decoration: none;
+    color: lighten($error-value-color, 12%);
+  }
+
   .fields-group {
     margin-bottom: 25px;
-  }
 
-  .input.radio_buttons .radio label {
-    margin-bottom: 5px;
-    font-family: inherit;
-    font-size: 14px;
-    color: $primary-text-color;
-    display: block;
-    width: auto;
+    .input:last-child {
+      margin-bottom: 0;
+    }
   }
 
-  .input.boolean {
-    margin-bottom: 5px;
+  .fields-row {
+    display: flex;
+    margin: 0 -10px;
+    padding-top: 5px;
+    margin-bottom: 25px;
 
-    label {
-      font-family: inherit;
-      font-size: 14px;
-      color: $primary-text-color;
-      display: block;
-      width: auto;
+    .input {
+      max-width: none;
     }
 
-    label.checkbox {
-      position: relative;
-      padding-left: 25px;
+    &__column {
+      box-sizing: border-box;
+      padding: 0 10px;
       flex: 1 1 auto;
+      min-height: 1px;
+
+      &-6 {
+        max-width: 50%;
+      }
     }
 
-    input[type=checkbox] {
-      position: absolute;
-      left: 0;
-      top: 5px;
-      margin: 0;
+    .fields-group:last-child,
+    .fields-row__column.fields-group {
+      margin-bottom: 0;
     }
 
-    .hint {
-      padding-left: 25px;
-      margin-left: 0;
+    @media screen and (max-width: $no-columns-breakpoint) {
+      display: block;
+      margin-bottom: 0;
+
+      &__column {
+        max-width: none;
+      }
+
+      .fields-group:last-child,
+      .fields-row__column.fields-group,
+      .fields-row__column {
+        margin-bottom: 25px;
+      }
     }
   }
 
+  .input.radio_buttons .radio label {
+    margin-bottom: 5px;
+    font-family: inherit;
+    font-size: 14px;
+    color: $primary-text-color;
+    display: block;
+    width: auto;
+  }
+
   .check_boxes {
     .checkbox {
       label {
@@ -236,12 +288,7 @@ code {
   input[type=email],
   input[type=password],
   textarea {
-    background: transparent;
     box-sizing: border-box;
-    border: 0;
-    border-bottom: 2px solid $ui-primary-color;
-    border-radius: 2px 2px 0 0;
-    padding: 7px 4px;
     font-size: 16px;
     color: $primary-text-color;
     display: block;
@@ -249,23 +296,31 @@ code {
     outline: 0;
     font-family: inherit;
     resize: vertical;
+    background: darken($ui-base-color, 10%);
+    border: 1px solid darken($ui-base-color, 14%);
+    border-radius: 4px;
+    padding: 10px;
 
     &:invalid {
       box-shadow: none;
     }
 
     &:focus:invalid {
-      border-bottom-color: lighten($error-red, 12%);
+      border-color: lighten($error-red, 12%);
     }
 
     &:required:valid {
-      border-bottom-color: $valid-value-color;
+      border-color: $valid-value-color;
+    }
+
+    &:hover {
+      border-color: darken($ui-base-color, 20%);
     }
 
     &:active,
     &:focus {
-      border-bottom-color: $highlight-text-color;
-      background: rgba($base-overlay-background, 0.1);
+      border-color: $highlight-text-color;
+      background: darken($ui-base-color, 8%);
     }
   }
 
@@ -349,22 +404,32 @@ code {
   }
 
   select {
+    appearance: none;
+    box-sizing: border-box;
     font-size: 16px;
-    max-height: 29px;
+    color: $primary-text-color;
+    display: block;
+    width: 100%;
+    outline: 0;
+    font-family: inherit;
+    resize: vertical;
+    background: darken($ui-base-color, 10%) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(lighten($ui-base-color, 12%))}'/></svg>") no-repeat right 8px center / auto 16px;
+    border: 1px solid darken($ui-base-color, 14%);
+    border-radius: 4px;
+    padding: 10px;
+    height: 41px;
   }
 
-  .input-with-append {
-    position: relative;
-
-    .input input {
-      padding-right: 142px;
+  .label_input {
+    &__wrapper {
+      position: relative;
     }
 
-    .append {
+    &__append {
       position: absolute;
-      right: 0;
-      top: 0;
-      padding: 7px 4px;
+      right: 1px;
+      top: 1px;
+      padding: 10px;
       padding-bottom: 9px;
       font-size: 16px;
       color: $dark-text-color;
@@ -383,7 +448,7 @@ code {
         right: 0;
         bottom: 1px;
         width: 5px;
-        background-image: linear-gradient(to right, rgba($ui-base-color, 0), $ui-base-color);
+        background-image: linear-gradient(to right, rgba(darken($ui-base-color, 10%), 0), darken($ui-base-color, 10%));
       }
     }
   }
@@ -459,6 +524,30 @@ code {
   }
 }
 
+.quick-nav {
+  list-style: none;
+  margin-bottom: 25px;
+  font-size: 14px;
+
+  li {
+    display: inline-block;
+    margin-right: 10px;
+  }
+
+  a {
+    color: $highlight-text-color;
+    text-transform: uppercase;
+    text-decoration: none;
+    font-weight: 700;
+
+    &:hover,
+    &:focus,
+    &:active {
+      color: lighten($highlight-text-color, 8%);
+    }
+  }
+}
+
 .oauth-prompt,
 .follow-prompt {
   margin-bottom: 30px;
@@ -632,3 +721,49 @@ code {
     font-family: 'mastodon-font-monospace', monospace;
   }
 }
+
+.input-copy {
+  background: darken($ui-base-color, 10%);
+  border: 1px solid darken($ui-base-color, 14%);
+  border-radius: 4px;
+  display: flex;
+  align-items: center;
+  padding-right: 4px;
+  position: relative;
+  top: 1px;
+  transition: border-color 300ms linear;
+
+  &__wrapper {
+    flex: 1 1 auto;
+  }
+
+  input[type=text] {
+    background: transparent;
+    border: 0;
+    padding: 10px;
+    font-size: 14px;
+    font-family: 'mastodon-font-monospace', monospace;
+  }
+
+  button {
+    flex: 0 0 auto;
+    margin: 4px;
+    text-transform: none;
+    font-weight: 400;
+    font-size: 14px;
+    padding: 7px 18px;
+    padding-bottom: 6px;
+    width: auto;
+    transition: background 300ms linear;
+  }
+
+  &.copied {
+    border-color: $valid-value-color;
+    transition: none;
+
+    button {
+      background: $valid-value-color;
+      transition: none;
+    }
+  }
+}
diff --git a/app/javascript/styles/mastodon/reset.scss b/app/javascript/styles/mastodon/reset.scss
index ff3b2c022..a140e8bc7 100644
--- a/app/javascript/styles/mastodon/reset.scss
+++ b/app/javascript/styles/mastodon/reset.scss
@@ -53,6 +53,11 @@ table {
   border-spacing: 0;
 }
 
+html {
+  scrollbar-face-color: lighten($ui-base-color, 4%);
+  scrollbar-track-color: rgba($base-overlay-background, 0.1);
+}
+
 ::-webkit-scrollbar {
   width: 12px;
   height: 12px;
diff --git a/app/lib/activitypub/activity/update.rb b/app/lib/activitypub/activity/update.rb
index 6eebc3b5c..67dae3f81 100644
--- a/app/lib/activitypub/activity/update.rb
+++ b/app/lib/activitypub/activity/update.rb
@@ -11,6 +11,7 @@ class ActivityPub::Activity::Update < ActivityPub::Activity
 
   def update_account
     return if @account.uri != object_uri
+
     ActivityPub::ProcessAccountService.new.call(@account.username, @account.domain, @object, signed_with_known_key: true)
   end
 end
diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb
index e1ab05cc0..8b694536c 100644
--- a/app/lib/formatter.rb
+++ b/app/lib/formatter.rb
@@ -23,7 +23,7 @@ class Formatter
 
     unless status.local?
       html = reformat(raw_content)
-      html = encode_custom_emojis(html, status.emojis) if options[:custom_emojify]
+      html = encode_custom_emojis(html, status.emojis, options[:autoplay]) if options[:custom_emojify]
       return html.html_safe # rubocop:disable Rails/OutputSafety
     end
 
@@ -33,7 +33,7 @@ class Formatter
     html = raw_content
     html = "RT @#{prepend_reblog} #{html}" if prepend_reblog
     html = encode_and_link_urls(html, linkable_accounts)
-    html = encode_custom_emojis(html, status.emojis) if options[:custom_emojify]
+    html = encode_custom_emojis(html, status.emojis, options[:autoplay]) if options[:custom_emojify]
     html = simple_format(html, {}, sanitize: false)
     html = html.delete("\n")
 
@@ -53,7 +53,7 @@ class Formatter
 
   def simplified_format(account, **options)
     html = account.local? ? linkify(account.note) : reformat(account.note)
-    html = encode_custom_emojis(html, account.emojis) if options[:custom_emojify]
+    html = encode_custom_emojis(html, account.emojis, options[:autoplay]) if options[:custom_emojify]
     html.html_safe # rubocop:disable Rails/OutputSafety
   end
 
@@ -61,22 +61,22 @@ class Formatter
     Sanitize.fragment(html, config)
   end
 
-  def format_spoiler(status)
+  def format_spoiler(status, **options)
     html = encode(status.spoiler_text)
-    html = encode_custom_emojis(html, status.emojis)
+    html = encode_custom_emojis(html, status.emojis, options[:autoplay])
     html.html_safe # rubocop:disable Rails/OutputSafety
   end
 
   def format_display_name(account, **options)
     html = encode(account.display_name.presence || account.username)
-    html = encode_custom_emojis(html, account.emojis) if options[:custom_emojify]
+    html = encode_custom_emojis(html, account.emojis, options[:autoplay]) if options[:custom_emojify]
     html.html_safe # rubocop:disable Rails/OutputSafety
   end
 
   def format_field(account, str, **options)
     return reformat(str).html_safe unless account.local? # rubocop:disable Rails/OutputSafety
     html = encode_and_link_urls(str, me: true)
-    html = encode_custom_emojis(html, account.emojis) if options[:custom_emojify]
+    html = encode_custom_emojis(html, account.emojis, options[:autoplay]) if options[:custom_emojify]
     html.html_safe # rubocop:disable Rails/OutputSafety
   end
 
@@ -120,10 +120,14 @@ class Formatter
     end
   end
 
-  def encode_custom_emojis(html, emojis)
+  def encode_custom_emojis(html, emojis, animate = false)
     return html if emojis.empty?
 
-    emoji_map = emojis.map { |e| [e.shortcode, full_asset_url(e.image.url(:static))] }.to_h
+    emoji_map = if animate
+                  emojis.map { |e| [e.shortcode, full_asset_url(e.image.url)] }.to_h
+                else
+                  emojis.map { |e| [e.shortcode, full_asset_url(e.image.url(:static))] }.to_h
+                end
 
     i                     = -1
     tag_open_index        = nil
diff --git a/app/lib/request.rb b/app/lib/request.rb
index 21bdaa700..36c211dbf 100644
--- a/app/lib/request.rb
+++ b/app/lib/request.rb
@@ -73,15 +73,15 @@ class Request
     algorithm = 'rsa-sha256'
     signature = Base64.strict_encode64(@keypair.sign(OpenSSL::Digest::SHA256.new, signed_string))
 
-    "keyId=\"#{key_id}\",algorithm=\"#{algorithm}\",headers=\"#{signed_headers}\",signature=\"#{signature}\""
+    "keyId=\"#{key_id}\",algorithm=\"#{algorithm}\",headers=\"#{signed_headers.keys.join(' ').downcase}\",signature=\"#{signature}\""
   end
 
   def signed_string
-    @headers.map { |key, value| "#{key.downcase}: #{value}" }.join("\n")
+    signed_headers.map { |key, value| "#{key.downcase}: #{value}" }.join("\n")
   end
 
   def signed_headers
-    @headers.keys.join(' ').downcase
+    @headers.without('User-Agent', 'Accept-Encoding')
   end
 
   def key_id
diff --git a/app/models/account.rb b/app/models/account.rb
index c84a7406d..d0c4c1a6d 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -226,11 +226,19 @@ class Account < ApplicationRecord
   end
 
   def fields_attributes=(attributes)
-    fields = []
+    fields     = []
+    old_fields = self[:fields] || []
 
     if attributes.is_a?(Hash)
       attributes.each_value do |attr|
         next if attr[:name].blank?
+
+        previous = old_fields.find { |item| item['value'] == attr[:value] }
+
+        if previous && previous['verified_at'].present?
+          attr[:verified_at] = previous['verified_at']
+        end
+
         fields << attr
       end
     end
@@ -238,13 +246,18 @@ class Account < ApplicationRecord
     self[:fields] = fields
   end
 
+  DEFAULT_FIELDS_SIZE = 4
+
   def build_fields
-    return if fields.size >= 4
+    return if fields.size >= DEFAULT_FIELDS_SIZE
+
+    tmp = self[:fields] || []
+
+    (DEFAULT_FIELDS_SIZE - tmp.size).times do
+      tmp << { name: '', value: '' }
+    end
 
-    raw_fields = self[:fields] || []
-    add_fields = 4 - raw_fields.size
-    add_fields.times { raw_fields << { name: '', value: '' } }
-    self.fields = raw_fields
+    self.fields = tmp
   end
 
   def magic_key
@@ -297,17 +310,32 @@ class Account < ApplicationRecord
   end
 
   class Field < ActiveModelSerializers::Model
-    attributes :name, :value, :account, :errors
+    attributes :name, :value, :verified_at, :account, :errors
+
+    def initialize(account, attributes)
+      @account     = account
+      @attributes  = attributes
+      @name        = attributes['name'].strip[0, 255]
+      @value       = attributes['value'].strip[0, 255]
+      @verified_at = attributes['verified_at']&.to_datetime
+      @errors      = {}
+    end
+
+    def verified?
+      verified_at.present?
+    end
+
+    def verifiable?
+      value.present? && value.start_with?('http://', 'https://')
+    end
 
-    def initialize(account, attr)
-      @account = account
-      @name    = attr['name'].strip[0, 255]
-      @value   = attr['value'].strip[0, 255]
-      @errors  = {}
+    def mark_verified!
+      @verified_at = Time.now.utc
+      @attributes['verified_at'] = @verified_at
     end
 
     def to_h
-      { name: @name, value: @value }
+      { name: @name, value: @value, verified_at: @verified_at }
     end
   end
 
diff --git a/app/models/concerns/remotable.rb b/app/models/concerns/remotable.rb
index c17f19a60..9372a963b 100644
--- a/app/models/concerns/remotable.rb
+++ b/app/models/concerns/remotable.rb
@@ -18,7 +18,7 @@ module Remotable
           return
         end
 
-        return if !%w(http https).include?(parsed_url.scheme) || parsed_url.host.empty? || self[attribute_name] == url
+        return if !%w(http https).include?(parsed_url.scheme) || parsed_url.host.blank? || self[attribute_name] == url
 
         begin
           Request.new(:get, url).perform do |response|
diff --git a/app/models/report.rb b/app/models/report.rb
index efe385b2d..2804020f5 100644
--- a/app/models/report.rb
+++ b/app/models/report.rb
@@ -60,6 +60,10 @@ class Report < ApplicationRecord
     !action_taken?
   end
 
+  def unresolved_siblings?
+    Report.where.not(id: id).where(target_account_id: target_account_id).unresolved.exists?
+  end
+
   def history
     time_range = created_at..updated_at
 
diff --git a/app/models/status.rb b/app/models/status.rb
index f9c0c68d9..028927cc3 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -248,7 +248,7 @@ class Status < ApplicationRecord
 
     def as_direct_timeline(account, limit = 20, max_id = nil, since_id = nil, cache_ids = false)
       # direct timeline is mix of direct message from_me and to_me.
-      # 2 querys are executed with pagination.
+      # 2 queries are executed with pagination.
       # constant expression using arel_table is required for partial index
 
       # _from_me part does not require any timeline filters
diff --git a/app/models/user.rb b/app/models/user.rb
index 9e529019c..6022a5eb0 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -224,6 +224,10 @@ class User < ApplicationRecord
     settings.notification_emails['digest']
   end
 
+  def allows_report_emails?
+    settings.notification_emails['report']
+  end
+
   def hides_network?
     @hides_network ||= settings.hide_network
   end
diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb
index 204a13b55..0845d0210 100644
--- a/app/serializers/initial_state_serializer.rb
+++ b/app/serializers/initial_state_serializer.rb
@@ -32,6 +32,7 @@ class InitialStateSerializer < ActiveModel::Serializer
       store[:auto_play_gif]           = object.current_account.user.setting_auto_play_gif
       store[:display_sensitive_media] = object.current_account.user.setting_display_sensitive_media
       store[:reduce_motion]           = object.current_account.user.setting_reduce_motion
+      store[:is_staff]                = object.current_account.user.staff?
     end
 
     store
diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb
index 3a724aa7c..d84b48afb 100644
--- a/app/serializers/rest/account_serializer.rb
+++ b/app/serializers/rest/account_serializer.rb
@@ -13,6 +13,10 @@ class REST::AccountSerializer < ActiveModel::Serializer
   class FieldSerializer < ActiveModel::Serializer
     attributes :name, :value
 
+    attribute :verified_at, if: :verifiable?
+
+    delegate :verifiable?, to: :object
+
     def value
       Formatter.instance.format_field(object.account, object.value)
     end
diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb
index 670a0e4d6..c77858f1d 100644
--- a/app/services/activitypub/process_account_service.rb
+++ b/app/services/activitypub/process_account_service.rb
@@ -34,6 +34,7 @@ class ActivityPub::ProcessAccountService < BaseService
     after_protocol_change! if protocol_changed?
     after_key_change! if key_changed? && !@options[:signed_with_known_key]
     check_featured_collection! if @account.featured_collection_url.present?
+    check_links! unless @account.fields.empty?
 
     @account
   rescue Oj::ParseError
@@ -99,6 +100,10 @@ class ActivityPub::ProcessAccountService < BaseService
     ActivityPub::SynchronizeFeaturedCollectionWorker.perform_async(@account.id)
   end
 
+  def check_links!
+    VerifyAccountLinksWorker.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/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb
index 560a81768..4169c685b 100644
--- a/app/services/fetch_link_card_service.rb
+++ b/app/services/fetch_link_card_service.rb
@@ -29,7 +29,7 @@ class FetchLinkCardService < BaseService
     end
 
     attach_card if @card&.persisted?
-  rescue HTTP::Error, Addressable::URI::InvalidURIError, Mastodon::LengthValidationError => e
+  rescue HTTP::Error, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e
     Rails.logger.debug "Error fetching link #{@url}: #{e}"
     nil
   end
@@ -87,34 +87,36 @@ class FetchLinkCardService < BaseService
   end
 
   def attempt_oembed
-    embed = FetchOEmbedService.new.call(@url, html: @html)
+    service = FetchOEmbedService.new
+    embed   = service.call(@url, html: @html)
+    url     = Addressable::URI.parse(service.endpoint_url)
 
     return false if embed.nil?
 
     @card.type          = embed[:type]
     @card.title         = embed[:title]         || ''
     @card.author_name   = embed[:author_name]   || ''
-    @card.author_url    = embed[:author_url]    || ''
+    @card.author_url    = embed[:author_url].present? ? (url + embed[:author_url]).to_s : ''
     @card.provider_name = embed[:provider_name] || ''
-    @card.provider_url  = embed[:provider_url]  || ''
+    @card.provider_url  = embed[:provider_url].present? ? (url + embed[:provider_url]).to_s : ''
     @card.width         = 0
     @card.height        = 0
 
     case @card.type
     when 'link'
-      @card.image_remote_url = embed[:thumbnail_url] if embed[:thumbnail_url].present?
+      @card.image_remote_url = (url + embed[:thumbnail_url]).to_s if embed[:thumbnail_url].present?
     when 'photo'
       return false if embed[:url].blank?
 
-      @card.embed_url        = embed[:url]
-      @card.image_remote_url = embed[:url]
+      @card.embed_url        = (url + embed[:url]).to_s
+      @card.image_remote_url = (url + embed[:url]).to_s
       @card.width            = embed[:width].presence  || 0
       @card.height           = embed[:height].presence || 0
     when 'video'
       @card.width            = embed[:width].presence  || 0
       @card.height           = embed[:height].presence || 0
       @card.html             = Formatter.instance.sanitize(embed[:html], Sanitize::Config::MASTODON_OEMBED)
-      @card.image_remote_url = embed[:thumbnail_url] if embed[:thumbnail_url].present?
+      @card.image_remote_url = (url + embed[:thumbnail_url]).to_s if embed[:thumbnail_url].present?
     when 'rich'
       # Most providers rely on <script> tags, which is a no-no
       return false
@@ -146,7 +148,7 @@ class FetchLinkCardService < BaseService
 
     @card.title            = meta_property(page, 'og:title').presence || page.at_xpath('//title')&.content || ''
     @card.description      = meta_property(page, 'og:description').presence || meta_property(page, 'description') || ''
-    @card.image_remote_url = meta_property(page, 'og:image') if meta_property(page, 'og:image')
+    @card.image_remote_url = (Addressable::URI.parse(@url) + meta_property(page, 'og:image')).to_s if meta_property(page, 'og:image')
 
     return if @card.title.blank? && @card.html.blank?
 
diff --git a/app/services/fetch_oembed_service.rb b/app/services/fetch_oembed_service.rb
index 998228517..9ddf9b13b 100644
--- a/app/services/fetch_oembed_service.rb
+++ b/app/services/fetch_oembed_service.rb
@@ -31,7 +31,7 @@ class FetchOEmbedService
 
     return if @endpoint_url.blank?
 
-    @endpoint_url = Addressable::URI.parse(@endpoint_url).to_s
+    @endpoint_url = (Addressable::URI.parse(@url) + @endpoint_url).to_s
   rescue Addressable::URI::InvalidURIError
     @endpoint_url = nil
   end
diff --git a/app/services/report_service.rb b/app/services/report_service.rb
index c06488a6d..057d05ab9 100644
--- a/app/services/report_service.rb
+++ b/app/services/report_service.rb
@@ -26,7 +26,10 @@ class ReportService < BaseService
   end
 
   def notify_staff!
+    return if @report.unresolved_siblings?
+
     User.staff.includes(:account).each do |u|
+      next unless u.allows_report_emails?
       AdminMailer.new_report(u.account, @report).deliver_later
     end
   end
diff --git a/app/services/update_account_service.rb b/app/services/update_account_service.rb
index 09ea377e7..ec69d944a 100644
--- a/app/services/update_account_service.rb
+++ b/app/services/update_account_service.rb
@@ -2,20 +2,26 @@
 
 class UpdateAccountService < BaseService
   def call(account, params, raise_error: false)
-    was_locked = account.locked
+    was_locked    = account.locked
     update_method = raise_error ? :update! : :update
+
     account.send(update_method, params).tap do |ret|
       next unless ret
+
       authorize_all_follow_requests(account) if was_locked && !account.locked
+      check_links(account)
     end
   end
 
   private
 
   def authorize_all_follow_requests(account)
-    follow_requests = FollowRequest.where(target_account: account)
-    AuthorizeFollowWorker.push_bulk(follow_requests) do |req|
+    AuthorizeFollowWorker.push_bulk(FollowRequest.where(target_account: account).select(:account_id, :target_account_id)) do |req|
       [req.account_id, req.target_account_id]
     end
   end
+
+  def check_links(account)
+    VerifyAccountLinksWorker.perform_async(account.id)
+  end
 end
diff --git a/app/services/verify_link_service.rb b/app/services/verify_link_service.rb
new file mode 100644
index 000000000..7d53bc255
--- /dev/null
+++ b/app/services/verify_link_service.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+class VerifyLinkService < BaseService
+  def call(field)
+    @link_back = ActivityPub::TagManager.instance.url_for(field.account)
+    @url       = field.value
+
+    perform_request!
+
+    return unless link_back_present?
+
+    field.mark_verified!
+    field.account.save!
+  rescue HTTP::Error, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e
+    Rails.logger.debug "Error fetching link #{@url}: #{e}"
+    nil
+  end
+
+  private
+
+  def perform_request!
+    @body = Request.new(:get, @url).add_headers('Accept' => 'text/html').perform do |res|
+      res.code != 200 ? nil : res.body_with_limit
+    end
+  end
+
+  def link_back_present?
+    return false if @body.empty?
+
+    links = Nokogiri::HTML(@body).xpath('//a[contains(concat(" ", normalize-space(@rel), " "), " me ")]|//link[contains(concat(" ", normalize-space(@rel), " "), " me ")]')
+
+    if links.any? { |link| link['href'] == @link_back }
+      true
+    elsif links.empty?
+      false
+    else
+      link_redirects_back?(links.first['href'])
+    end
+  end
+
+  def link_redirects_back?(test_url)
+    redirect_to_url = Request.new(:head, test_url, follow: false).perform do |res|
+      res.headers['Location']
+    end
+
+    redirect_to_url == @link_back
+  end
+end
diff --git a/app/views/about/_registration.html.haml b/app/views/about/_registration.html.haml
index 6ca1d7129..ee4f8fe2e 100644
--- a/app/views/about/_registration.html.haml
+++ b/app/views/about/_registration.html.haml
@@ -1,13 +1,10 @@
 = simple_form_for(new_user, url: user_registration_path) do |f|
   = f.simple_fields_for :account do |account_fields|
-    .input-with-append
-      = account_fields.input :username, autofocus: true, placeholder: t('simple_form.labels.defaults.username'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off' }
-      .append
-        = "@#{site_hostname}"
+    = account_fields.input :username, wrapper: :with_label, autofocus: true, label: false, required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off', placeholder: t('simple_form.labels.defaults.username') }, append: "@#{site_hostname}", hint: false
 
-  = f.input :email, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' }
-  = f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' }
-  = f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }
+  = f.input :email, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' }, hint: false
+  = f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' }, hint: false
+  = f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }, hint: false
 
   .actions
     = f.button :button, t('auth.register'), type: :submit, class: 'button button-primary'
diff --git a/app/views/about/more.html.haml b/app/views/about/more.html.haml
index 46eb17cf7..99028935f 100644
--- a/app/views/about/more.html.haml
+++ b/app/views/about/more.html.haml
@@ -24,7 +24,7 @@
             %span= t 'about.status_count_after', count: @instance_presenter.status_count
         .row__mascot
           .landing-page__mascot
-            = image_tag asset_pack_path('elephant_ui_plane.svg')
+            = image_tag asset_pack_path('elephant_ui_plane.svg'), alt: ''
 
   .column-2
     .landing-page__information.contact-widget
diff --git a/app/views/about/show.html.haml b/app/views/about/show.html.haml
index fd76a6b7d..48435fe9c 100644
--- a/app/views/about/show.html.haml
+++ b/app/views/about/show.html.haml
@@ -62,7 +62,7 @@
                   %span= t 'about.status_count_after', count: @instance_presenter.status_count
               .row__mascot
                 .landing-page__mascot
-                  = image_tag asset_pack_path('elephant_ui_plane.svg')
+                  = image_tag asset_pack_path('elephant_ui_plane.svg'), alt: ''
 
       - else
         .column-2.non-preview
@@ -94,7 +94,7 @@
                   %span= t 'about.status_count_after', count: @instance_presenter.status_count
               .row__mascot
                 .landing-page__mascot
-                  = image_tag asset_pack_path('elephant_ui_plane.svg')
+                  = image_tag asset_pack_path('elephant_ui_plane.svg'), alt: ''
 
       - if Setting.timeline_preview
         .column-3
diff --git a/app/views/accounts/_bio.html.haml b/app/views/accounts/_bio.html.haml
index 4e674beff..2ea34a048 100644
--- a/app/views/accounts/_bio.html.haml
+++ b/app/views/accounts/_bio.html.haml
@@ -4,8 +4,11 @@
       - account.fields.each do |field|
         %dl
           %dt.emojify{ title: field.name }= Formatter.instance.format_field(account, field.name, custom_emojify: true)
-          %dd.emojify{ title: field.value }= Formatter.instance.format_field(account, field.value, custom_emojify: true)
-
+          %dd{ title: field.value, class: custom_field_classes(field) }
+            - if field.verified?
+              %span.verified__mark{ title: t('accounts.link_verified_on', date: l(field.verified_at)) }
+                = fa_icon 'check'
+            = Formatter.instance.format_field(account, field.value, custom_emojify: true)
   = account_badge(account)
 
   - if account.note.present?
diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml
index f09beff98..3da270d27 100644
--- a/app/views/accounts/_header.html.haml
+++ b/app/views/accounts/_header.html.haml
@@ -1,9 +1,12 @@
-.public-account-header
+.public-account-header{:class => ("inactive" if account.moved?)}
   .public-account-header__image
     = image_tag account.header.url, class: 'parallax'
   .public-account-header__bar
     = link_to short_account_url(account), class: 'avatar' do
-      = image_tag account.avatar.url
+      - if current_account&.user&.setting_auto_play_gif
+        = image_tag account.avatar_original_url
+      - else
+        = image_tag account.avatar_static_url
     .public-account-header__tabs
       .public-account-header__tabs__name
         %h1
@@ -13,7 +16,7 @@
             = fa_icon('lock') if account.locked?
       .public-account-header__tabs__tabs
         .details-counters
-          .counter{ class: active_nav_class(short_account_url(account)) }
+          .counter{ class: active_nav_class(short_account_url(account)) + active_nav_class(short_account_with_replies_url(account)) + active_nav_class(short_account_media_url(account)) }
             = link_to short_account_url(account), class: 'u-url u-uid', title: number_with_delimiter(account.statuses_count) do
               %span.counter-number= number_to_human account.statuses_count, strip_insignificant_zeros: true
               %span.counter-label= t('accounts.posts', count: account.statuses_count)
diff --git a/app/views/admin/change_emails/show.html.haml b/app/views/admin/change_emails/show.html.haml
index a661b1ad6..6febef9b1 100644
--- a/app/views/admin/change_emails/show.html.haml
+++ b/app/views/admin/change_emails/show.html.haml
@@ -2,6 +2,11 @@
   = t('admin.accounts.change_email.title', username: @account.acct)
 
 = simple_form_for @user, url: admin_account_change_email_path(@account.id) do |f|
-  = f.input :email, wrapper: :with_label, disabled: true, label: t('admin.accounts.change_email.current_email')
-  = f.input :unconfirmed_email, wrapper: :with_label, label: t('admin.accounts.change_email.new_email')
-  = f.button :submit, class: "button", value: t('admin.accounts.change_email.submit')
+  .fields-group
+    = f.input :email, wrapper: :with_label, disabled: true, label: t('admin.accounts.change_email.current_email')
+
+  .fields-group
+    = f.input :unconfirmed_email, wrapper: :with_label, label: t('admin.accounts.change_email.new_email')
+
+  .actions
+    = f.button :submit, class: "button", value: t('admin.accounts.change_email.submit')
diff --git a/app/views/admin/custom_emojis/new.html.haml b/app/views/admin/custom_emojis/new.html.haml
index 672afe435..e15a07cb8 100644
--- a/app/views/admin/custom_emojis/new.html.haml
+++ b/app/views/admin/custom_emojis/new.html.haml
@@ -5,8 +5,9 @@
   = render 'shared/error_messages', object: @custom_emoji
 
   .fields-group
-    = f.input :shortcode, placeholder: t('admin.custom_emojis.shortcode'), hint: t('admin.custom_emojis.shortcode_hint')
-    = f.input :image, input_html: { accept: 'image/png' }, hint: t('admin.custom_emojis.image_hint')
+    = f.input :shortcode, wrapper: :with_label, label: t('admin.custom_emojis.shortcode'), hint: t('admin.custom_emojis.shortcode_hint')
+  .fields-group
+    = f.input :image, wrapper: :with_label, input_html: { accept: 'image/png' }, hint: t('admin.custom_emojis.image_hint')
 
   .actions
     = f.button :button, t('admin.custom_emojis.upload'), type: :submit
diff --git a/app/views/admin/domain_blocks/new.html.haml b/app/views/admin/domain_blocks/new.html.haml
index 38fa90169..6e514f833 100644
--- a/app/views/admin/domain_blocks/new.html.haml
+++ b/app/views/admin/domain_blocks/new.html.haml
@@ -4,14 +4,15 @@
 = simple_form_for @domain_block, url: admin_domain_blocks_path do |f|
   = render 'shared/error_messages', object: @domain_block
 
-  %p.hint= t('.hint')
+  .fields-row
+    .fields-row__column.fields-row__column-6.fields-group
+      = f.input :domain, wrapper: :with_label, label: t('admin.domain_blocks.domain'), hint: t('.hint'), required: true
 
-  = f.input :domain, placeholder: t('admin.domain_blocks.domain')
-  = f.input :severity, collection: DomainBlock.severities.keys, wrapper: :with_label, include_blank: false, label_method: lambda { |type| t(".severity.#{type}") }
+    .fields-row__column.fields-row__column-6.fields-group
+      = f.input :severity, collection: DomainBlock.severities.keys, wrapper: :with_label, include_blank: false, label_method: lambda { |type| t(".severity.#{type}") }, hint: t('.severity.desc_html')
 
-  %p.hint= t('.severity.desc_html')
-
-  = f.input :reject_media, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_media'), hint: I18n.t('admin.domain_blocks.reject_media_hint')
+  .fields-group
+    = f.input :reject_media, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_media'), hint: I18n.t('admin.domain_blocks.reject_media_hint')
 
   .actions
     = f.button :button, t('.create'), type: :submit
diff --git a/app/views/admin/email_domain_blocks/new.html.haml b/app/views/admin/email_domain_blocks/new.html.haml
index bcae867d9..f372fa512 100644
--- a/app/views/admin/email_domain_blocks/new.html.haml
+++ b/app/views/admin/email_domain_blocks/new.html.haml
@@ -4,7 +4,8 @@
 = simple_form_for @email_domain_block, url: admin_email_domain_blocks_path do |f|
   = render 'shared/error_messages', object: @email_domain_block
 
-  = f.input :domain, placeholder: t('admin.email_domain_blocks.domain')
+  .fields-group
+    = f.input :domain, wrapper: :with_label, label: t('admin.email_domain_blocks.domain')
 
   .actions
     = f.button :button, t('.create'), type: :submit
diff --git a/app/views/admin/reports/_account.html.haml b/app/views/admin/reports/_account.html.haml
deleted file mode 100644
index 9ac161c9c..000000000
--- a/app/views/admin/reports/_account.html.haml
+++ /dev/null
@@ -1,19 +0,0 @@
-- size ||= 36
-
-.account.compact
-  .account__wrapper
-    - if account.nil?
-      .account__display-name
-        .account__avatar-wrapper
-          .account__avatar{ style: "background-image: url(#{full_asset_url('avatars/original/missing.png', skip_pipeline: true)}); width: #{size}px; height: #{size}px; background-size: #{size}px #{size}px" }
-        %span.display-name
-          %strong= t 'about.contact_missing'
-          %span.display-name__account= t 'about.contact_unavailable'
-    - else
-      = link_to TagManager.instance.url_for(account), class: 'account__display-name' do
-        .account__avatar-wrapper
-          .account__avatar{ style: "background-image: url(#{account.avatar.url}); width: #{size}px; height: #{size}px; background-size: #{size}px #{size}px" }
-        %span.display-name
-          %bdi
-            %strong.display-name__html.emojify= display_name(account, custom_emojify: true)
-          %span.display-name__account @#{account.acct}
diff --git a/app/views/admin/reports/_report.html.haml b/app/views/admin/reports/_report.html.haml
deleted file mode 100644
index 7b25c924b..000000000
--- a/app/views/admin/reports/_report.html.haml
+++ /dev/null
@@ -1,29 +0,0 @@
-%tr
-  %td.id
-    = "##{report.id}"
-  %td.target
-    = admin_account_link_to report.target_account
-  %td.reporter
-    - if report.account.local?
-      = admin_account_link_to report.account
-    - else
-      = report.account.domain
-  %td
-    %div{ title: report.comment }
-      = truncate(report.comment, length: 30, separator: ' ')
-    %div
-      - unless report.statuses.empty?
-        %span{ title: t('admin.accounts.statuses') }
-          = fa_icon('comment')
-          = report.statuses.count
-      - unless report.media_attachments.empty?
-        %span{ title: t('admin.accounts.media_attachments') }
-          = fa_icon('camera')
-          = report.media_attachments.count
-  %td
-    - if report.assigned_account.nil?
-      \-
-    - else
-      = admin_account_link_to report.assigned_account
-  %td
-    = table_link_to 'circle', t('admin.reports.view'), admin_report_path(report)
diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml
index 44a531f2c..d73faccb0 100644
--- a/app/views/admin/reports/index.html.haml
+++ b/app/views/admin/reports/index.html.haml
@@ -8,17 +8,45 @@
       %li= filter_link_to t('admin.reports.unresolved'), resolved: nil
       %li= filter_link_to t('admin.reports.resolved'), resolved: '1'
 
-.table-wrapper
-  %table.table
-    %thead
-      %tr
-        %th= t('admin.reports.id')
-        %th= t('admin.reports.target')
-        %th= t('admin.reports.reported_by')
-        %th= t('admin.reports.report_contents')
-        %th= t('admin.reports.assigned')
-        %th
-    %tbody
-      = render @reports
+- @reports.group_by(&:target_account_id).each do |target_account_id, reports|
+  - target_account = reports.first.target_account
+  .report-card
+    .report-card__profile
+      = account_link_to target_account, '', size: 36, path: admin_account_path(target_account.id)
+      .report-card__profile__stats
+        = link_to pluralize(target_account.targeted_moderation_notes.count, t('admin.reports.account.note')), admin_account_path(target_account.id)
+        %br/
+        - if target_account.suspended?
+          %span.red= t('admin.accounts.suspended')
+        - elsif target_account.silenced?
+          %span.red= t('admin.accounts.silenced')
+        - elsif target_account.user&.disabled?
+          %span.red= t('admin.accounts.disabled')
+        - else
+          %span.neutral= t('admin.accounts.no_limits_imposed')
+    .report-card__summary
+      - reports.each do |report|
+        .report-card__summary__item
+          .report-card__summary__item__reported-by
+            - if report.account.local?
+              = admin_account_link_to report.account
+            - else
+              = report.account.domain
+          .report-card__summary__item__content
+            = link_to admin_report_path(report) do
+              .one-line= report.comment.presence || t('admin.reports.comment.none')
 
+              %span.report-card__summary__item__content__icon{ title: t('admin.accounts.statuses') }
+                = fa_icon('comment')
+                = report.statuses.count
+
+              %span.report-card__summary__item__content__icon{ title: t('admin.accounts.media_attachments') }
+                = fa_icon('camera')
+                = report.media_attachments.count
+
+          .report-card__summary__item__assigned
+            - if report.assigned_account.present?
+              = admin_account_link_to report.assigned_account
+            - else
+              \-
 = paginate @reports
diff --git a/app/views/admin/settings/edit.html.haml b/app/views/admin/settings/edit.html.haml
index abe7ecf32..b4abbf815 100644
--- a/app/views/admin/settings/edit.html.haml
+++ b/app/views/admin/settings/edit.html.haml
@@ -2,24 +2,37 @@
   = t('admin.settings.title')
 
 = simple_form_for @admin_settings, url: admin_settings_path, html: { method: :patch } do |f|
-  .actions.actions--top
-    = f.button :button, t('generic.save_changes'), type: :submit
 
   .fields-group
-    = f.input :site_title, placeholder: t('admin.settings.site_title')
-    = f.input :site_short_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_short_description.title'), hint: t('admin.settings.site_short_description.desc_html'), input_html: { rows: 2 }
+    = f.input :site_title, wrapper: :with_label, label: t('admin.settings.site_title')
+
+  .fields-group
+    = f.input :flavour, collection: Themes.instance.flavours, label_method: lambda { |flavour| I18n.t("flavours.#{flavour}.name", default: flavour) }, wrapper: :with_label, include_blank: false
+
+  .fields-row
+    .fields-row__column.fields-row__column-6.fields-group
+      = f.input :site_contact_username, wrapper: :with_label, label: t('admin.settings.contact_information.username')
+    .fields-row__column.fields-row__column-6.fields-group
+      = f.input :site_contact_email, wrapper: :with_label, label: t('admin.settings.contact_information.email')
+
+  .fields-group
     = f.input :site_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description.title'), hint: t('admin.settings.site_description.desc_html'), input_html: { rows: 4 }
-    = f.input :site_contact_username, placeholder: t('admin.settings.contact_information.username')
-    = f.input :site_contact_email, placeholder: t('admin.settings.contact_information.email')
 
-  %hr/
+  .fields-group
+    = f.input :site_short_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_short_description.title'), hint: t('admin.settings.site_short_description.desc_html'), input_html: { rows: 2 }
+
+  .fields-row
+    .fields-row__column.fields-row__column-6.fields-group
+      = f.input :thumbnail, as: :file, wrapper: :with_block_label, label: t('admin.settings.thumbnail.title'), hint: t('admin.settings.thumbnail.desc_html')
+    .fields-row__column.fields-row__column-6.fields-group
+      = f.input :hero, as: :file, wrapper: :with_block_label, label: t('admin.settings.hero.title'), hint: t('admin.settings.hero.desc_html')
+
+  %hr.spacer/
 
   .fields-group
-    = f.input :flavour, collection: Themes.instance.flavours, label_method: lambda { |flavour| I18n.t("flavours.#{flavour}.name", default: flavour) }, wrapper: :with_label, include_blank: false
-    = f.input :thumbnail, as: :file, wrapper: :with_block_label, label: t('admin.settings.thumbnail.title'), hint: t('admin.settings.thumbnail.desc_html')
-    = f.input :hero, as: :file, wrapper: :with_block_label, label: t('admin.settings.hero.title'), hint: t('admin.settings.hero.desc_html')
+    = 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')
 
-  %hr/
+  %hr.spacer/
 
   .fields-group
     = f.input :timeline_preview, as: :boolean, wrapper: :with_label, label: t('admin.settings.timeline_preview.title'), hint: t('admin.settings.timeline_preview.desc_html')
@@ -37,34 +50,24 @@
     = f.input :open_deletion, as: :boolean, wrapper: :with_label, label: t('admin.settings.registrations.deletion.title'), hint: t('admin.settings.registrations.deletion.desc_html')
 
   .fields-group
-    = f.input :closed_registrations_message, as: :text, wrapper: :with_block_label, label: t('admin.settings.registrations.closed_message.title'), hint: t('admin.settings.registrations.closed_message.desc_html'), input_html: { rows: 8 }
-
-  %hr/
-
-  .fields-group
-    = f.input :min_invite_role, wrapper: :with_label, collection: %i(disabled user moderator admin), label: t('admin.settings.registrations.min_invite_role.title'), label_method: lambda { |role| role == :disabled ? t('admin.settings.registrations.min_invite_role.disabled') : t("admin.accounts.roles.#{role}") }, as: :radio_buttons, include_blank: false, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
-
-  %hr/
+    = f.input :activity_api_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.activity_api_enabled.title'), hint: t('admin.settings.activity_api_enabled.desc_html')
 
   .fields-group
-    = f.input :site_extended_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description_extended.title'), hint: t('admin.settings.site_description_extended.desc_html'), input_html: { rows: 8 }
-    = f.input :site_terms, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_terms.title'), hint: t('admin.settings.site_terms.desc_html'), input_html: { rows: 8 }
-    = f.input :custom_css, wrapper: :with_block_label, as: :text, input_html: { rows: 8 }, label: t('admin.settings.custom_css.title'), hint: t('admin.settings.custom_css.desc_html')
-  %hr/
+    = f.input :peers_api_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.peers_api_enabled.title'), hint: t('admin.settings.peers_api_enabled.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')
+    = f.input :preview_sensitive_media, as: :boolean, wrapper: :with_label, label: t('admin.settings.preview_sensitive_media.title'), hint: t('admin.settings.preview_sensitive_media.desc_html')
 
-  %hr/
+  %hr.spacer/
 
   .fields-group
-    = f.input :activity_api_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.activity_api_enabled.title'), hint: t('admin.settings.activity_api_enabled.desc_html')
+    = f.input :min_invite_role, wrapper: :with_label, collection: %i(disabled user moderator admin), label: t('admin.settings.registrations.min_invite_role.title'), label_method: lambda { |role| role == :disabled ? t('admin.settings.registrations.min_invite_role.disabled') : t("admin.accounts.roles.#{role}") }, include_blank: false, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
 
   .fields-group
-    = f.input :peers_api_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.peers_api_enabled.title'), hint: t('admin.settings.peers_api_enabled.desc_html')
-
-  .fields-group
-    = f.input :preview_sensitive_media, as: :boolean, wrapper: :with_label, label: t('admin.settings.preview_sensitive_media.title'), hint: t('admin.settings.preview_sensitive_media.desc_html')
+    = f.input :closed_registrations_message, as: :text, wrapper: :with_block_label, label: t('admin.settings.registrations.closed_message.title'), hint: t('admin.settings.registrations.closed_message.desc_html'), input_html: { rows: 8 }
+    = f.input :site_extended_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description_extended.title'), hint: t('admin.settings.site_description_extended.desc_html'), input_html: { rows: 8 }
+    = f.input :site_terms, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_terms.title'), hint: t('admin.settings.site_terms.desc_html'), input_html: { rows: 8 }
+    = f.input :custom_css, wrapper: :with_block_label, as: :text, input_html: { rows: 8 }, label: t('admin.settings.custom_css.title'), hint: t('admin.settings.custom_css.desc_html')
 
   .actions
     = f.button :button, t('generic.save_changes'), type: :submit
diff --git a/app/views/admin/statuses/show.html.haml b/app/views/admin/statuses/show.html.haml
new file mode 100644
index 000000000..a7a392272
--- /dev/null
+++ b/app/views/admin/statuses/show.html.haml
@@ -0,0 +1,27 @@
+- content_for :page_title do
+  = t('admin.statuses.title')
+  \-
+  = "@#{@account.acct}"
+
+.filters
+  .back-link{ style: 'flex: 1 1 auto; text-align: right' }
+    = link_to admin_account_path(@account.id) do
+      %i.fa.fa-chevron-left.fa-fw
+      = t('admin.statuses.back_to_account')
+
+%hr.spacer/
+
+= form_for(@form, url: admin_account_statuses_path(@account.id)) do |f|
+  = hidden_field_tag :page, params[:page]
+  = hidden_field_tag :media, params[:media]
+
+  .batch-table
+    .batch-table__toolbar
+      %label.batch-table__toolbar__select.batch-checkbox-all
+        = check_box_tag :batch_checkbox_all, nil, false
+      .batch-table__toolbar__actions
+        = f.button safe_join([fa_icon('eye-slash'), t('admin.statuses.batch.nsfw_on')]), name: :nsfw_on, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
+        = f.button safe_join([fa_icon('eye'), t('admin.statuses.batch.nsfw_off')]), name: :nsfw_off, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
+        = f.button safe_join([fa_icon('trash'), t('admin.statuses.batch.delete')]), name: :delete, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
+    .batch-table__body
+      = render partial: 'admin/reports/status', collection: @statuses, locals: { f: f }
diff --git a/app/views/auth/confirmations/finish_signup.html.haml b/app/views/auth/confirmations/finish_signup.html.haml
index 4b5161d6b..9d09b74e1 100644
--- a/app/views/auth/confirmations/finish_signup.html.haml
+++ b/app/views/auth/confirmations/finish_signup.html.haml
@@ -8,7 +8,8 @@
         = msg
         %br
 
-  = f.input :email
+  .fields-group
+    = f.input :email, wrapper: :with_label, required: true, hint: false
 
   .actions
     = f.submit t('auth.confirm_email'), class: 'button'
diff --git a/app/views/auth/confirmations/new.html.haml b/app/views/auth/confirmations/new.html.haml
index 07ca4a7da..4a1bedaa4 100644
--- a/app/views/auth/confirmations/new.html.haml
+++ b/app/views/auth/confirmations/new.html.haml
@@ -4,7 +4,8 @@
 = simple_form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f|
   = render 'shared/error_messages', object: resource
 
-  = f.input :email, autofocus: true, required: true, placeholder: t('simple_form.labels.defaults.email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }
+  .fields-group
+    = f.input :email, autofocus: true, wrapper: :with_label, label: t('simple_form.labels.defaults.email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }, hint: false
 
   .actions
     = f.button :button, t('auth.resend_confirmation'), type: :submit
diff --git a/app/views/auth/passwords/edit.html.haml b/app/views/auth/passwords/edit.html.haml
index 53d1769d6..383d44f00 100644
--- a/app/views/auth/passwords/edit.html.haml
+++ b/app/views/auth/passwords/edit.html.haml
@@ -7,8 +7,10 @@
   - if !use_seamless_external_login? || resource.encrypted_password.present?
     = f.input :reset_password_token, as: :hidden
 
-    = f.input :password, autofocus: true, placeholder: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off' }
-    = f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'off' }
+    .fields-group
+      = f.input :password, wrapper: :with_label, autofocus: true, label: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off' }, required: true
+    .fields-group
+      = f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'off' }, required: true
 
     .actions
       = f.button :button, t('auth.set_new_password'), type: :submit
diff --git a/app/views/auth/passwords/new.html.haml b/app/views/auth/passwords/new.html.haml
index f4d22031a..bae5b24ba 100644
--- a/app/views/auth/passwords/new.html.haml
+++ b/app/views/auth/passwords/new.html.haml
@@ -4,7 +4,8 @@
 = simple_form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f|
   = render 'shared/error_messages', object: resource
 
-  = f.input :email, autofocus: true, required: true, placeholder: t('simple_form.labels.defaults.email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }
+  .fields-group
+    = f.input :email, autofocus: true, wrapper: :with_label, label: t('simple_form.labels.defaults.email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }, hint: false
 
   .actions
     = f.button :button, t('auth.reset_password'), type: :submit
diff --git a/app/views/auth/registrations/edit.html.haml b/app/views/auth/registrations/edit.html.haml
index 05fc7df31..7fc08a23b 100644
--- a/app/views/auth/registrations/edit.html.haml
+++ b/app/views/auth/registrations/edit.html.haml
@@ -1,24 +1,32 @@
 - content_for :page_title do
   = t('auth.security')
 
-%h4= t('auth.change_password')
 = simple_form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put, class: 'auth_edit' }) do |f|
   = render 'shared/error_messages', object: resource
 
   - if !use_seamless_external_login? || resource.encrypted_password.present?
-    = f.input :email, placeholder: t('simple_form.labels.defaults.email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }
-    = f.input :password, placeholder: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off' }
-    = f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'off' }
-    = f.input :current_password, placeholder: t('simple_form.labels.defaults.current_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.current_password'), :autocomplete => 'off' }
+    .fields-group
+      = f.input :email, wrapper: :with_label, input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }, required: true, hint: false
+
+    .fields-group
+      = f.input :password, wrapper: :with_label, input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off' }, hint: false
+
+    .fields-group
+      = f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'off' }
+
+    .fields-group
+      = f.input :current_password, wrapper: :with_label, input_html: { 'aria-label' => t('simple_form.labels.defaults.current_password'), :autocomplete => 'off' }, required: true
 
     .actions
       = f.button :button, t('generic.save_changes'), type: :submit
   - else
     %p.hint= t('users.seamless_external_login')
 
+%hr.spacer/
+
 = render 'sessions'
 
 - if open_deletion?
-
+  %hr.spacer/
   %h4= t('auth.delete_account')
   %p.muted-hint= t('auth.delete_account_html', path: settings_delete_path)
diff --git a/app/views/auth/registrations/new.html.haml b/app/views/auth/registrations/new.html.haml
index 200ed42de..72ce8e531 100644
--- a/app/views/auth/registrations/new.html.haml
+++ b/app/views/auth/registrations/new.html.haml
@@ -13,18 +13,22 @@
       = render 'application/card', account: @invite.user.account
 
   = f.simple_fields_for :account do |ff|
-    .input-with-append
-      = ff.input :username, autofocus: true, placeholder: t('simple_form.labels.defaults.username'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off' }
-      .append
-        = "@#{site_hostname}"
-
-  = f.input :email, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' }
-  = f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' }
-  = f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }
+    .fields-group
+      = ff.input :username, wrapper: :with_label, autofocus: true, label: t('simple_form.labels.defaults.username'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off' }, append: "@#{site_hostname}", hint: t('simple_form.hints.defaults.username', domain: site_hostname)
+
+  .fields-group
+    = f.input :email, wrapper: :with_label, label: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' }
+
+  .fields-group
+    = f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' }
+  .fields-group
+    = f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }
+
   = f.input :invite_code, as: :hidden
 
+  %p.hint= t('auth.agreement_html', rules_path: about_more_path, terms_path: terms_path)
+
   .actions
     = f.button :button, t('auth.register'), type: :submit
 
-  %p.hint.subtle-hint=t('auth.agreement_html', rules_path: about_more_path, terms_path: terms_path)
 .form-footer= render 'auth/shared/links'
diff --git a/app/views/auth/sessions/new.html.haml b/app/views/auth/sessions/new.html.haml
index 0c9f9d5fe..ceb169408 100644
--- a/app/views/auth/sessions/new.html.haml
+++ b/app/views/auth/sessions/new.html.haml
@@ -5,11 +5,13 @@
   = render partial: 'shared/og'
 
 = simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
-  - if use_seamless_external_login?
-    = f.input :email, autofocus: true, placeholder: t('simple_form.labels.defaults.username_or_email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username_or_email') }
-  - else
-    = f.input :email, autofocus: true, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }
-  = f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' }
+  .fields-group
+    - if use_seamless_external_login?
+      = f.input :email, autofocus: true, wrapper: :with_label, label: t('simple_form.labels.defaults.username_or_email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.username_or_email') }, hint: false
+    - else
+      = f.input :email, autofocus: true, wrapper: :with_label, label: t('simple_form.labels.defaults.email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }, hint: false
+  .fields-group
+    = f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' }, hint: false
 
   .actions
     = f.button :button, t('auth.login'), type: :submit
diff --git a/app/views/auth/sessions/two_factor.html.haml b/app/views/auth/sessions/two_factor.html.haml
index 1af3193ae..4e6bbd7a9 100644
--- a/app/views/auth/sessions/two_factor.html.haml
+++ b/app/views/auth/sessions/two_factor.html.haml
@@ -4,7 +4,8 @@
 = simple_form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f|
   %p.hint{ style: 'margin-bottom: 25px' }= t('simple_form.hints.sessions.otp')
 
-  = f.input :otp_attempt, type: :number, placeholder: t('simple_form.labels.defaults.otp_attempt'), input_html: { 'aria-label' => t('simple_form.labels.defaults.otp_attempt'), :autocomplete => 'off' }, required: true, autofocus: true
+  .fields-group
+    = f.input :otp_attempt, type: :number, wrapper: :with_label, label: t('simple_form.labels.defaults.otp_attempt'), input_html: { 'aria-label' => t('simple_form.labels.defaults.otp_attempt'), :autocomplete => 'off' }, autofocus: true
 
   .actions
     = f.button :button, t('auth.login'), type: :submit
diff --git a/app/views/filters/_fields.html.haml b/app/views/filters/_fields.html.haml
index a5a3f0337..fb94a07fc 100644
--- a/app/views/filters/_fields.html.haml
+++ b/app/views/filters/_fields.html.haml
@@ -1,14 +1,16 @@
-.fields-group
-  = f.input :phrase, as: :string, wrapper: :with_block_label
+.fields-row
+  .fields-row__column.fields-row__column-6.fields-group
+    = f.input :phrase, as: :string, wrapper: :with_label, hint: false
+  .fields-row__column.fields-row__column-6.fields-group
+    = f.input :expires_in, wrapper: :with_label, collection: [30.minutes, 1.hour, 6.hours, 12.hours, 1.day, 1.week].map(&:to_i), label_method: lambda { |i| I18n.t("invites.expires_in.#{i}") }, prompt: I18n.t('invites.expires_in_prompt')
 
 .fields-group
   = f.input :context, wrapper: :with_block_label, collection: CustomFilter::VALID_CONTEXTS, as: :check_boxes, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', label_method: lambda { |context| I18n.t("filters.contexts.#{context}") }, include_blank: false
 
+%hr.spacer/
+
 .fields-group
   = f.input :irreversible, wrapper: :with_label
 
 .fields-group
   = f.input :whole_word, wrapper: :with_label
-
-.fields-group
-  = f.input :expires_in, wrapper: :with_label, collection: [30.minutes, 1.hour, 6.hours, 12.hours, 1.day, 1.week].map(&:to_i), label_method: lambda { |i| I18n.t("invites.expires_in.#{i}") }, prompt: I18n.t('invites.expires_in_prompt')
diff --git a/app/views/invites/_form.html.haml b/app/views/invites/_form.html.haml
index 42a107bb2..3a2a5ef0e 100644
--- a/app/views/invites/_form.html.haml
+++ b/app/views/invites/_form.html.haml
@@ -1,9 +1,11 @@
 = simple_form_for(@invite, url: controller.is_a?(Admin::InvitesController) ? admin_invites_path : invites_path) do |f|
   = render 'shared/error_messages', object: @invite
 
-  .fields-group
-    = f.input :max_uses, wrapper: :with_label, collection: [1, 5, 10, 25, 50, 100], label_method: lambda { |num| I18n.t('invites.max_uses', count: num) }, prompt: I18n.t('invites.max_uses_prompt')
-    = f.input :expires_in, wrapper: :with_label, collection: [30.minutes, 1.hour, 6.hours, 12.hours, 1.day, 1.week].map(&:to_i), label_method: lambda { |i| I18n.t("invites.expires_in.#{i}") }, prompt: I18n.t('invites.expires_in_prompt')
+  .fields-row
+    .fields-row__column.fields-row__column-6.fields-group
+      = f.input :max_uses, wrapper: :with_label, collection: [1, 5, 10, 25, 50, 100], label_method: lambda { |num| I18n.t('invites.max_uses', count: num) }, prompt: I18n.t('invites.max_uses_prompt')
+    .fields-row__column.fields-row__column-6.fields-group
+      = f.input :expires_in, wrapper: :with_label, collection: [30.minutes, 1.hour, 6.hours, 12.hours, 1.day, 1.week].map(&:to_i), label_method: lambda { |i| I18n.t("invites.expires_in.#{i}") }, prompt: I18n.t('invites.expires_in_prompt')
 
   .fields-group
     = f.input :autofollow, wrapper: :with_label
diff --git a/app/views/invites/index.html.haml b/app/views/invites/index.html.haml
index f4c5047fa..fb827f6e6 100644
--- a/app/views/invites/index.html.haml
+++ b/app/views/invites/index.html.haml
@@ -6,7 +6,7 @@
 
   = render 'form'
 
-  %hr/
+  %hr.spacer/
 
 %table.table
   %thead
diff --git a/app/views/layouts/modal.html.haml b/app/views/layouts/modal.html.haml
index a86b4fd3f..b6e33ca91 100644
--- a/app/views/layouts/modal.html.haml
+++ b/app/views/layouts/modal.html.haml
@@ -5,7 +5,7 @@
       .name
         = t 'users.signed_in_as'
         %span.username @#{current_account.local_username_and_domain}
-      = link_to destroy_user_session_path, method: :delete, class: 'logout-link icon-button' do
+      = link_to destroy_user_session_path(continue: true), method: :delete, class: 'logout-link icon-button' do
         = fa_icon 'sign-out'
 
   .container-alt= yield
diff --git a/app/views/settings/applications/_fields.html.haml b/app/views/settings/applications/_fields.html.haml
index db90df349..6a2863b20 100644
--- a/app/views/settings/applications/_fields.html.haml
+++ b/app/views/settings/applications/_fields.html.haml
@@ -1,6 +1,8 @@
 .fields-group
-  = f.input :name, placeholder: t('activerecord.attributes.doorkeeper/application.name')
-  = f.input :website, placeholder: t('activerecord.attributes.doorkeeper/application.website')
+  = f.input :name, wrapper: :with_label, label: t('activerecord.attributes.doorkeeper/application.name')
+
+.fields-group
+  = f.input :website, wrapper: :with_label, label: t('activerecord.attributes.doorkeeper/application.website')
 
 .fields-group
   = f.input :redirect_uri, wrapper: :with_block_label, label: t('activerecord.attributes.doorkeeper/application.redirect_uri'), hint: t('doorkeeper.applications.help.redirect_uri')
diff --git a/app/views/settings/exports/show.html.haml b/app/views/settings/exports/show.html.haml
index 792dccd9e..6c030b1ab 100644
--- a/app/views/settings/exports/show.html.haml
+++ b/app/views/settings/exports/show.html.haml
@@ -9,7 +9,7 @@
         %td= number_to_human_size @export.total_storage
         %td
       %tr
-        %th= t('accounts.statuses', count: @export.total_statuses)
+        %th= t('accounts.posts', count: @export.total_statuses)
         %td= number_with_delimiter @export.total_statuses
         %td
       %tr
diff --git a/app/views/settings/imports/show.html.haml b/app/views/settings/imports/show.html.haml
index 2b43cb134..4512fc714 100644
--- a/app/views/settings/imports/show.html.haml
+++ b/app/views/settings/imports/show.html.haml
@@ -2,10 +2,8 @@
   = t('settings.import')
 
 = simple_form_for @import, url: settings_import_path do |f|
-  %p.hint= t('imports.preface')
-
   .field-group
-    = f.input :type, collection: Import.types.keys, wrapper: :with_label, include_blank: false, label_method: lambda { |type| I18n.t("imports.types.#{type}") }, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
+    = f.input :type, collection: Import.types.keys, wrapper: :with_block_label, include_blank: false, label_method: lambda { |type| I18n.t("imports.types.#{type}") }, hint: t('imports.preface')
 
   .field-group
     = f.input :data, wrapper: :with_block_label, hint: t('simple_form.hints.imports.data')
diff --git a/app/views/settings/notifications/show.html.haml b/app/views/settings/notifications/show.html.haml
index b718b62df..8aaac043b 100644
--- a/app/views/settings/notifications/show.html.haml
+++ b/app/views/settings/notifications/show.html.haml
@@ -12,6 +12,9 @@
       = ff.input :favourite, as: :boolean, wrapper: :with_label
       = ff.input :mention, as: :boolean, wrapper: :with_label
 
+      - if current_user.staff?
+        = ff.input :report, as: :boolean, wrapper: :with_label
+
   .fields-group
     = f.simple_fields_for :notification_emails, hash_to_object(current_user.settings.notification_emails) do |ff|
       = ff.input :digest, as: :boolean, wrapper: :with_label
diff --git a/app/views/settings/preferences/show.html.haml b/app/views/settings/preferences/show.html.haml
index 9092780c3..bb267db8a 100644
--- a/app/views/settings/preferences/show.html.haml
+++ b/app/views/settings/preferences/show.html.haml
@@ -1,29 +1,32 @@
 - content_for :page_title do
   = t('settings.preferences')
 
+%ul.quick-nav
+  %li= link_to t('preferences.languages'), '#settings_languages'
+  %li= link_to t('preferences.publishing'), '#settings_publishing'
+  %li= link_to t('preferences.other'), '#settings_other'
+  %li= link_to t('preferences.web'), '#settings_web'
+
 = simple_form_for current_user, url: settings_preferences_path, html: { method: :put } do |f|
   = render 'shared/error_messages', object: current_user
 
-  .actions.actions--top
-    = f.button :button, t('generic.save_changes'), type: :submit
-
-  %h4= t 'preferences.languages'
+  .fields-row#settings_languages
+    .fields-group.fields-row__column.fields-row__column-6
+      = f.input :locale, collection: I18n.available_locales, wrapper: :with_label, include_blank: false, label_method: lambda { |locale| human_locale(locale) }, selected: I18n.locale
+    .fields-group.fields-row__column.fields-row__column-6
+      = f.input :setting_default_language, collection: [nil] + filterable_languages.sort, wrapper: :with_label, label_method: lambda { |locale| locale.nil? ? I18n.t('statuses.language_detection') : human_locale(locale) }, required: false, include_blank: false
 
   .fields-group
-    = f.input :locale, collection: I18n.available_locales, wrapper: :with_label, include_blank: false, label_method: lambda { |locale| human_locale(locale) }, selected: I18n.locale
-
-    = f.input :setting_default_language, collection: [nil] + filterable_languages.sort, wrapper: :with_label, label_method: lambda { |locale| locale.nil? ? I18n.t('statuses.language_detection') : human_locale(locale) }, required: false, include_blank: false
-
     = f.input :chosen_languages, collection: filterable_languages.sort, wrapper: :with_block_label, include_blank: false, label_method: lambda { |locale| human_locale(locale) }, required: false, as: :check_boxes, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
 
-  %h4= t 'preferences.publishing'
+  %hr#settings_publishing/
 
   .fields-group
-    = f.input :setting_default_privacy, collection: Status.visibilities.keys - ['direct'], wrapper: :with_label, include_blank: false, label_method: lambda { |visibility| safe_join([I18n.t("statuses.visibilities.#{visibility}"), content_tag(:span, I18n.t("statuses.visibilities.#{visibility}_long"), class: 'hint')]) }, required: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
+    = f.input :setting_default_privacy, collection: Status.visibilities.keys - ['direct'], wrapper: :with_floating_label, include_blank: false, label_method: lambda { |visibility| safe_join([I18n.t("statuses.visibilities.#{visibility}"), content_tag(:span, I18n.t("statuses.visibilities.#{visibility}_long"), class: 'hint')]) }, required: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
 
     = f.input :setting_default_sensitive, as: :boolean, wrapper: :with_label
 
-  %h4= t 'preferences.other'
+  %hr#settings_other/
 
   .fields-group
     = f.input :setting_noindex, as: :boolean, wrapper: :with_label
@@ -31,7 +34,7 @@
   .fields-group
     = f.input :setting_hide_network, as: :boolean, wrapper: :with_label
 
-  %h4= t 'preferences.web'
+  %hr#settings_web/
 
   .fields-group
     = f.input :setting_unfollow_modal, as: :boolean, wrapper: :with_label
diff --git a/app/views/settings/profiles/show.html.haml b/app/views/settings/profiles/show.html.haml
index 1acbb9b8a..6b61fa9c9 100644
--- a/app/views/settings/profiles/show.html.haml
+++ b/app/views/settings/profiles/show.html.haml
@@ -4,16 +4,21 @@
 = simple_form_for @account, url: settings_profile_path, html: { method: :put } do |f|
   = render 'shared/error_messages', object: @account
 
-  .fields-group
-    = f.input :display_name, placeholder: t('simple_form.labels.defaults.display_name'), hint: t('simple_form.hints.defaults.display_name', count: 30 - @account.display_name.size).html_safe
-    = f.input :note, placeholder: t('simple_form.labels.defaults.note'), hint: t('simple_form.hints.defaults.note', count: 500 - @account.note.size).html_safe
+  .fields-row
+    .fields-row__column.fields-group.fields-row__column-6
+      = f.input :display_name, wrapper: :with_label, hint: t('simple_form.hints.defaults.display_name', count: 30 - @account.display_name.size).html_safe
+      = f.input :note, wrapper: :with_label, hint: t('simple_form.hints.defaults.note', count: 500 - @account.note.size).html_safe
 
-  = render 'application/card', account: @account
+  .fields-row
+    .fields-row__column.fields-row__column-6
+      = render 'application/card', account: @account
 
-  .fields-group
-    = f.input :avatar, wrapper: :with_label, input_html: { accept: AccountAvatar::IMAGE_MIME_TYPES.join(',') }, hint: t('simple_form.hints.defaults.avatar', dimensions: '400x400', size: number_to_human_size(AccountAvatar::LIMIT))
+    .fields-row__column.fields-group.fields-row__column-6
+      = f.input :avatar, wrapper: :with_label, input_html: { accept: AccountAvatar::IMAGE_MIME_TYPES.join(',') }, hint: t('simple_form.hints.defaults.avatar', dimensions: '400x400', size: number_to_human_size(AccountAvatar::LIMIT))
+
+      = f.input :header, wrapper: :with_label, input_html: { accept: AccountHeader::IMAGE_MIME_TYPES.join(',') }, hint: t('simple_form.hints.defaults.header', dimensions: '1500x500', size: number_to_human_size(AccountHeader::LIMIT))
 
-    = f.input :header, wrapper: :with_label, input_html: { accept: AccountHeader::IMAGE_MIME_TYPES.join(',') }, hint: t('simple_form.hints.defaults.header', dimensions: '1500x500', size: number_to_human_size(AccountHeader::LIMIT))
+  %hr.spacer/
 
   .fields-group
     = f.input :locked, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.locked')
@@ -21,15 +26,27 @@
   .fields-group
     = f.input :bot, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.bot')
 
-  .fields-group
-    .input.with_block_label
-      %label= t('simple_form.labels.defaults.fields')
-      %span.hint= t('simple_form.hints.defaults.fields')
+  %hr.spacer/
+
+  .fields-row
+    .fields-row__column.fields-group.fields-row__column-6
+      .input.with_block_label
+        %label= t('simple_form.labels.defaults.fields')
+        %span.hint= t('simple_form.hints.defaults.fields')
 
-      = f.simple_fields_for :fields do |fields_f|
-        .row
-          = fields_f.input :name, placeholder: t('simple_form.labels.account.fields.name')
-          = fields_f.input :value, placeholder: t('simple_form.labels.account.fields.value')
+        = f.simple_fields_for :fields do |fields_f|
+          .row
+            = fields_f.input :name, placeholder: t('simple_form.labels.account.fields.name')
+            = fields_f.input :value, placeholder: t('simple_form.labels.account.fields.value')
+
+    .fields-row__column.fields-group.fields-row__column-6
+      %h6= t('verification.verification')
+      %p.hint= t('verification.explanation_html')
+
+      .input-copy
+        .input-copy__wrapper
+          %input{ type: :text, maxlength: '999', spellcheck: 'false', readonly: 'true', value: link_to('Mastodon', ActivityPub::TagManager.instance.url_for(@account), rel: 'me').to_str }
+        %button{ type: :button }= t('generic.copy')
 
   .actions
     = f.button :button, t('generic.save_changes'), type: :submit
@@ -38,3 +55,9 @@
 
 %h6= t('auth.migrate_account')
 %p.muted-hint= t('auth.migrate_account_html', path: settings_migration_path)
+
+- if open_deletion?
+  %hr.spacer/
+
+  %h6= t('auth.delete_account')
+  %p.muted-hint= t('auth.delete_account_html', path: settings_delete_path)
diff --git a/app/views/settings/two_factor_authentication/confirmations/new.html.haml b/app/views/settings/two_factor_authentication/confirmations/new.html.haml
index fd4a3e768..e64155299 100644
--- a/app/views/settings/two_factor_authentication/confirmations/new.html.haml
+++ b/app/views/settings/two_factor_authentication/confirmations/new.html.haml
@@ -11,7 +11,8 @@
       %p.hint= t('two_factor_authentication.manual_instructions')
       %samp.qr-alternative__code= current_user.otp_secret.scan(/.{4}/).join(' ')
 
-  = f.input :code, hint: t('two_factor_authentication.code_hint'), placeholder: t('simple_form.labels.defaults.otp_attempt'), input_html: { :autocomplete => 'off' }
+  .fields-group
+    = f.input :code, wrapper: :with_label, hint: t('two_factor_authentication.code_hint'), label: t('simple_form.labels.defaults.otp_attempt'), input_html: { :autocomplete => 'off' }, required: true
 
   .actions
     = f.button :button, t('two_factor_authentication.enable'), type: :submit
diff --git a/app/views/settings/two_factor_authentications/show.html.haml b/app/views/settings/two_factor_authentications/show.html.haml
index 67a64a046..259bcd1ef 100644
--- a/app/views/settings/two_factor_authentications/show.html.haml
+++ b/app/views/settings/two_factor_authentications/show.html.haml
@@ -10,7 +10,7 @@
   %hr/
 
   = simple_form_for @confirmation, url: settings_two_factor_authentication_path, method: :delete do |f|
-    = f.input :code, hint: t('two_factor_authentication.code_hint'), placeholder: t('simple_form.labels.defaults.otp_attempt'), input_html: { :autocomplete => 'off' }
+    = f.input :code, wrapper: :with_label, hint: t('two_factor_authentication.code_hint'), label: t('simple_form.labels.defaults.otp_attempt'), input_html: { :autocomplete => 'off' }, required: true
 
     .actions
       = f.button :button, t('two_factor_authentication.disable'), type: :submit
diff --git a/app/views/stream_entries/_detailed_status.html.haml b/app/views/stream_entries/_detailed_status.html.haml
index 7843005c1..d0d9cc5fc 100644
--- a/app/views/stream_entries/_detailed_status.html.haml
+++ b/app/views/stream_entries/_detailed_status.html.haml
@@ -1,10 +1,13 @@
 .detailed-status.detailed-status--flex
   = link_to TagManager.instance.url_for(status.account), class: 'detailed-status__display-name p-author h-card', target: stream_link_target, rel: 'noopener' do
     .detailed-status__display-avatar
-      = image_tag status.account.avatar.url(:original), width: 48, height: 48, alt: '', class: 'account__avatar u-photo'
+      - if current_account&.user&.setting_auto_play_gif || autoplay
+        = image_tag status.account.avatar_original_url, width: 48, height: 48, alt: '', class: 'account__avatar u-photo'
+      - else
+        = image_tag status.account.avatar_static_url, width: 48, height: 48, alt: '', class: 'account__avatar u-photo'
     %span.display-name
       %bdi
-        %strong.display-name__html.p-name.emojify= display_name(status.account, custom_emojify: true)
+        %strong.display-name__html.p-name.emojify= display_name(status.account, custom_emojify: true, autoplay: autoplay)
       %span.display-name__account
         = acct(status.account)
         = fa_icon('lock') if status.account.locked?
@@ -14,16 +17,16 @@
   .status__content.emojify<
     - if status.spoiler_text?
       %p{ style: 'margin-bottom: 0' }<
-        %span.p-summary> #{Formatter.instance.format_spoiler(status)}&nbsp;
+        %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}&nbsp;
         %a.status__content__spoiler-link{ href: '#' }= t('statuses.show_more')
-    .e-content{ lang: status.language, style: "display: #{status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }= Formatter.instance.format(status, custom_emojify: true)
+    .e-content{ lang: status.language, style: "display: #{status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }= Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay)
 
   - if !status.media_attachments.empty?
     - if status.media_attachments.first.video?
       - video = status.media_attachments.first
       = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 670, height: 380, detailed: true, inline: true, alt: video.description
     - else
-      = react_component :media_gallery, height: 380, sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, standalone: true, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, 'reduceMotion': current_account&.user&.setting_reduce_motion, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }
+      = react_component :media_gallery, height: 380, sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, standalone: true, 'autoPlayGif': current_account&.user&.setting_auto_play_gif || autoplay, 'reduceMotion': current_account&.user&.setting_reduce_motion, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }
   - elsif status.preview_cards.first
     = react_component :card, 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_cards.first, serializer: REST::PreviewCardSerializer).as_json
 
diff --git a/app/views/stream_entries/_simple_status.html.haml b/app/views/stream_entries/_simple_status.html.haml
index effd4826e..4484a7e62 100644
--- a/app/views/stream_entries/_simple_status.html.haml
+++ b/app/views/stream_entries/_simple_status.html.haml
@@ -7,27 +7,30 @@
     = link_to TagManager.instance.url_for(status.account), class: 'status__display-name p-author h-card', target: stream_link_target, rel: 'noopener' do
       .status__avatar
         %div
-          = image_tag status.account.avatar(:original), width: 48, height: 48, alt: '', class: 'u-photo account__avatar'
+          - if current_account&.user&.setting_auto_play_gif || autoplay
+            = image_tag status.account.avatar_original_url, width: 48, height: 48, alt: '', class: 'u-photo account__avatar'
+          - else
+            = image_tag status.account.avatar_static_url, width: 48, height: 48, alt: '', class: 'u-photo account__avatar'
       %span.display-name
         %bdi
-          %strong.display-name__html.p-name.emojify= display_name(status.account, custom_emojify: true)
+          %strong.display-name__html.p-name.emojify= display_name(status.account, custom_emojify: true, autoplay: autoplay)
         %span.display-name__account
           = acct(status.account)
           = fa_icon('lock') if status.account.locked?
   .status__content.emojify<
     - if status.spoiler_text?
       %p{ style: 'margin-bottom: 0' }<
-        %span.p-summary> #{Formatter.instance.format_spoiler(status)}&nbsp;
+        %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}&nbsp;
         %a.status__content__spoiler-link{ href: '#' }= t('statuses.show_more')
     .e-content{ lang: status.language, style: "display: #{status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }<
-      = Formatter.instance.format(status, custom_emojify: true)
+      = Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay)
 
   - unless status.media_attachments.empty?
     - if status.media_attachments.first.video?
       - video = status.media_attachments.first
       = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 610, height: 343, inline: true, alt: video.description
     - else
-      = react_component :media_gallery, height: 343, sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }
+      = react_component :media_gallery, height: 343, sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, 'autoPlayGif': current_account&.user&.setting_auto_play_gif || autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }
 
   .status__action-bar
     .status__action-bar__counter
diff --git a/app/views/stream_entries/_status.html.haml b/app/views/stream_entries/_status.html.haml
index 92003a48f..83887cd87 100644
--- a/app/views/stream_entries/_status.html.haml
+++ b/app/views/stream_entries/_status.html.haml
@@ -5,6 +5,7 @@
   is_successor    ||= false
   direct_reply_id ||= false
   parent_id       ||= false
+  autoplay        ||= current_account&.user&.setting_auto_play_gif
   is_direct_parent  = direct_reply_id == status.id
   is_direct_child   = parent_id == status.in_reply_to_id
   centered        ||= include_threads && !is_predecessor && !is_successor
@@ -18,7 +19,7 @@
     .entry{ class: entry_classes }
       = link_to_more TagManager.instance.url_for(@next_ancestor)
 
-  = render partial: 'stream_entries/status', collection: @ancestors, as: :status, locals: { is_predecessor: true, direct_reply_id: status.in_reply_to_id }
+  = render partial: 'stream_entries/status', collection: @ancestors, as: :status, locals: { is_predecessor: true, direct_reply_id: status.in_reply_to_id }, autoplay: autoplay
 
 .entry{ class: entry_classes }
 
@@ -38,14 +39,14 @@
       %span
         = t('stream_entries.pinned')
 
-  = render (centered ? 'stream_entries/detailed_status' : 'stream_entries/simple_status'), status: status.proper
+  = render (centered ? 'stream_entries/detailed_status' : 'stream_entries/simple_status'), status: status.proper, autoplay: autoplay
 
 - if include_threads
   - if @since_descendant_thread_id
     .entry{ class: entry_classes }
       = link_to_more short_account_status_url(status.account.username, status, max_descendant_thread_id: @since_descendant_thread_id + 1)
   - @descendant_threads.each do |thread|
-    = render partial: 'stream_entries/status', collection: thread[:statuses], as: :status, locals: { is_successor: true, parent_id: status.id }
+    = render partial: 'stream_entries/status', collection: thread[:statuses], as: :status, locals: { is_successor: true, parent_id: status.id }, autoplay: autoplay
 
     - if thread[:next_status]
       .entry{ class: entry_classes }
diff --git a/app/views/stream_entries/embed.html.haml b/app/views/stream_entries/embed.html.haml
index d20c1e93e..4871c101e 100644
--- a/app/views/stream_entries/embed.html.haml
+++ b/app/views/stream_entries/embed.html.haml
@@ -1,3 +1,3 @@
 - cache @stream_entry.activity do
   .activity-stream.activity-stream--headless
-    = render "stream_entries/#{@type}", @type.to_sym => @stream_entry.activity, centered: true
+    = render "stream_entries/#{@type}", @type.to_sym => @stream_entry.activity, centered: true, autoplay: @autoplay
diff --git a/app/workers/verify_account_links_worker.rb b/app/workers/verify_account_links_worker.rb
new file mode 100644
index 000000000..901498583
--- /dev/null
+++ b/app/workers/verify_account_links_worker.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class VerifyAccountLinksWorker
+  include Sidekiq::Worker
+
+  sidekiq_options queue: 'pull', retry: false, unique: :until_executed
+
+  def perform(account_id)
+    account = Account.find(account_id)
+
+    account.fields.each do |field|
+      next unless !field.verified? && field.verifiable?
+      VerifyLinkService.new.call(field)
+    end
+
+    account.save! if account.changed?
+  rescue ActiveRecord::RecordNotFound
+    true
+  end
+end